Commit 0fa00e84 authored by Alfredo Sumaran's avatar Alfredo Sumaran

Merge branch 'reset-new-branch-button' into 'master'

Reset New branch button when issue state changes

Closes #21292

See merge request !5962
parents defdf45a 83c9d8cf
...@@ -20,57 +20,60 @@ class Issue { ...@@ -20,57 +20,60 @@ class Issue {
}); });
Issue.initIssueBtnEventListeners(); Issue.initIssueBtnEventListeners();
} }
Issue.$btnNewBranch = $('#new-branch');
Issue.initMergeRequests(); Issue.initMergeRequests();
Issue.initRelatedBranches(); Issue.initRelatedBranches();
Issue.initCanCreateBranch(); Issue.initCanCreateBranch();
} }
static initIssueBtnEventListeners() { static initIssueBtnEventListeners() {
var issueFailMessage; const issueFailMessage = 'Unable to update this issue at this time.';
issueFailMessage = 'Unable to update this issue at this time.';
return $('a.btn-close, a.btn-reopen').on('click', function(e) { const closeButtons = $('a.btn-close');
var $this, isClose, shouldSubmit, url; const isClosedBadge = $('div.status-box-closed');
const isOpenBadge = $('div.status-box-open');
const projectIssuesCounter = $('.issue_counter');
const reopenButtons = $('a.btn-reopen');
return closeButtons.add(reopenButtons).on('click', function(e) {
var $this, shouldSubmit, url;
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
$this = $(this); $this = $(this);
isClose = $this.hasClass('btn-close');
shouldSubmit = $this.hasClass('btn-comment'); shouldSubmit = $this.hasClass('btn-comment');
if (shouldSubmit) { if (shouldSubmit) {
Issue.submitNoteForm($this.closest('form')); Issue.submitNoteForm($this.closest('form'));
} }
$this.prop('disabled', true); $this.prop('disabled', true);
Issue.setNewBranchButtonState(true, null);
url = $this.attr('href'); url = $this.attr('href');
return $.ajax({ return $.ajax({
type: 'PUT', type: 'PUT',
url: url, url: url
error: function(jqXHR, textStatus, errorThrown) { }).fail(function(jqXHR, textStatus, errorThrown) {
var issueStatus; new Flash(issueFailMessage);
issueStatus = isClose ? 'close' : 'open'; Issue.initCanCreateBranch();
return new Flash(issueFailMessage, 'alert'); }).done(function(data, textStatus, jqXHR) {
},
success: function(data, textStatus, jqXHR) {
if ('id' in data) { if ('id' in data) {
$(document).trigger('issuable:change'); $(document).trigger('issuable:change');
let total = Number($('.issue_counter').text().replace(/[^\d]/, ''));
if (isClose) { const isClosed = $this.hasClass('btn-close');
$('a.btn-close').addClass('hidden'); closeButtons.toggleClass('hidden', isClosed);
$('a.btn-reopen').removeClass('hidden'); reopenButtons.toggleClass('hidden', !isClosed);
$('div.status-box-closed').removeClass('hidden'); isClosedBadge.toggleClass('hidden', !isClosed);
$('div.status-box-open').addClass('hidden'); isOpenBadge.toggleClass('hidden', isClosed);
total -= 1;
} else { let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, ''));
$('a.btn-reopen').addClass('hidden'); numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
$('a.btn-close').removeClass('hidden'); projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues));
$('div.status-box-closed').addClass('hidden');
$('div.status-box-open').removeClass('hidden');
total += 1;
}
$('.issue_counter').text(gl.text.addDelimiter(total));
} else { } else {
new Flash(issueFailMessage, 'alert'); new Flash(issueFailMessage);
}
return $this.prop('disabled', false);
} }
$this.prop('disabled', false);
Issue.initCanCreateBranch();
}); });
}); });
} }
...@@ -86,9 +89,9 @@ class Issue { ...@@ -86,9 +89,9 @@ class Issue {
static initMergeRequests() { static initMergeRequests() {
var $container; var $container;
$container = $('#merge-requests'); $container = $('#merge-requests');
return $.getJSON($container.data('url')).error(function() { return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load referenced merge requests', 'alert'); return new Flash('Failed to load referenced merge requests');
}).success(function(data) { }).done(function(data) {
if ('html' in data) { if ('html' in data) {
return $container.html(data.html); return $container.html(data.html);
} }
...@@ -98,9 +101,9 @@ class Issue { ...@@ -98,9 +101,9 @@ class Issue {
static initRelatedBranches() { static initRelatedBranches() {
var $container; var $container;
$container = $('#related-branches'); $container = $('#related-branches');
return $.getJSON($container.data('url')).error(function() { return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load related branches', 'alert'); return new Flash('Failed to load related branches');
}).success(function(data) { }).done(function(data) {
if ('html' in data) { if ('html' in data) {
return $container.html(data.html); return $container.html(data.html);
} }
...@@ -108,24 +111,27 @@ class Issue { ...@@ -108,24 +111,27 @@ class Issue {
} }
static initCanCreateBranch() { static initCanCreateBranch() {
var $container;
$container = $('#new-branch');
// If the user doesn't have the required permissions the container isn't // If the user doesn't have the required permissions the container isn't
// rendered at all. // rendered at all.
if ($container.length === 0) { if (Issue.$btnNewBranch.length === 0) {
return; return;
} }
return $.getJSON($container.data('path')).error(function() { return $.getJSON(Issue.$btnNewBranch.data('path')).fail(function() {
$container.find('.unavailable').show(); Issue.setNewBranchButtonState(false, false);
return new Flash('Failed to check if a new branch can be created.', 'alert'); new Flash('Failed to check if a new branch can be created.');
}).success(function(data) { }).done(function(data) {
if (data.can_create_branch) { Issue.setNewBranchButtonState(false, data.can_create_branch);
$container.find('.available').show();
} else {
return $container.find('.unavailable').show();
}
}); });
} }
static setNewBranchButtonState(isPending, canCreate) {
if (Issue.$btnNewBranch.length === 0) {
return;
}
Issue.$btnNewBranch.find('.available').toggle(!isPending && canCreate);
Issue.$btnNewBranch.find('.unavailable').toggle(!isPending && !canCreate);
}
} }
export default Issue; export default Issue;
---
title: Reset New branch button when issue state changes
merge_request: 5962
author: winniehell
/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */ /* eslint-disable space-before-function-paren, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */
import Issue from '~/issue'; import Issue from '~/issue';
require('~/lib/utils/text_utility'); require('~/lib/utils/text_utility');
describe('Issue', function() { describe('Issue', function() {
var INVALID_URL = 'http://goesnowhere.nothing/whereami'; let $boxClosed, $boxOpen, $btnClose, $btnReopen;
var $boxClosed, $boxOpen, $btnClose, $btnReopen;
preloadFixtures('issues/closed-issue.html.raw'); preloadFixtures('issues/closed-issue.html.raw');
preloadFixtures('issues/issue-with-task-list.html.raw'); preloadFixtures('issues/issue-with-task-list.html.raw');
preloadFixtures('issues/open-issue.html.raw'); preloadFixtures('issues/open-issue.html.raw');
function expectErrorMessage() { function expectErrorMessage() {
var $flashMessage = $('div.flash-alert'); const $flashMessage = $('div.flash-alert');
expect($flashMessage).toExist(); expect($flashMessage).toExist();
expect($flashMessage).toBeVisible(); expect($flashMessage).toBeVisible();
expect($flashMessage).toHaveText('Unable to update this issue at this time.'); expect($flashMessage).toHaveText('Unable to update this issue at this time.');
...@@ -26,10 +25,28 @@ describe('Issue', function() { ...@@ -26,10 +25,28 @@ describe('Issue', function() {
expectVisibility($btnReopen, !isIssueOpen); expectVisibility($btnReopen, !isIssueOpen);
} }
function expectPendingRequest(req, $triggeredButton) { function expectNewBranchButtonState(isPending, canCreate) {
expect(req.type).toBe('PUT'); if (Issue.$btnNewBranch.length === 0) {
expect(req.url).toBe($triggeredButton.attr('href')); return;
expect($triggeredButton).toHaveProp('disabled', true); }
const $available = Issue.$btnNewBranch.find('.available');
expect($available).toHaveText('New branch');
if (!isPending && canCreate) {
expect($available).toBeVisible();
} else {
expect($available).toBeHidden();
}
const $unavailable = Issue.$btnNewBranch.find('.unavailable');
expect($unavailable).toHaveText('New branch unavailable');
if (!isPending && !canCreate) {
expect($unavailable).toBeVisible();
} else {
expect($unavailable).toBeHidden();
}
} }
function expectVisibility($element, shouldBeVisible) { function expectVisibility($element, shouldBeVisible) {
...@@ -81,100 +98,107 @@ describe('Issue', function() { ...@@ -81,100 +98,107 @@ describe('Issue', function() {
}); });
}); });
describe('close issue', function() { [true, false].forEach((isIssueInitiallyOpen) => {
describe(`with ${isIssueInitiallyOpen ? 'open' : 'closed'} issue`, function() {
const action = isIssueInitiallyOpen ? 'close' : 'reopen';
function ajaxSpy(req) {
if (req.url === this.$triggeredButton.attr('href')) {
expect(req.type).toBe('PUT');
expect(this.$triggeredButton).toHaveProp('disabled', true);
expectNewBranchButtonState(true, false);
return this.issueStateDeferred;
} else if (req.url === Issue.$btnNewBranch.data('path')) {
expect(req.type).toBe('get');
expectNewBranchButtonState(true, false);
return this.canCreateBranchDeferred;
}
expect(req.url).toBe('unexpected');
return null;
}
beforeEach(function() { beforeEach(function() {
if (isIssueInitiallyOpen) {
loadFixtures('issues/open-issue.html.raw'); loadFixtures('issues/open-issue.html.raw');
} else {
loadFixtures('issues/closed-issue.html.raw');
}
findElements(); findElements();
this.issue = new Issue(); this.issue = new Issue();
expectIssueState(isIssueInitiallyOpen);
this.$triggeredButton = isIssueInitiallyOpen ? $btnClose : $btnReopen;
this.$projectIssuesCounter = $('.issue_counter');
this.$projectIssuesCounter.text('1,001');
this.issueStateDeferred = new jQuery.Deferred();
this.canCreateBranchDeferred = new jQuery.Deferred();
expectIssueState(true); spyOn(jQuery, 'ajax').and.callFake(ajaxSpy.bind(this));
}); });
it('closes an issue', function() { it(`${action}s the issue`, function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) { this.$triggeredButton.trigger('click');
expectPendingRequest(req, $btnClose); this.issueStateDeferred.resolve({
req.success({
id: 34 id: 34
}); });
this.canCreateBranchDeferred.resolve({
can_create_branch: !isIssueInitiallyOpen
}); });
$btnClose.trigger('click'); expectIssueState(!isIssueInitiallyOpen);
expect(this.$triggeredButton).toHaveProp('disabled', false);
expectIssueState(false); expect(this.$projectIssuesCounter.text()).toBe(isIssueInitiallyOpen ? '1,000' : '1,002');
expect($btnClose).toHaveProp('disabled', false); expectNewBranchButtonState(false, !isIssueInitiallyOpen);
expect($('.issue_counter')).toHaveText(0);
}); });
it('fails to close an issue with success:false', function() { it(`fails to ${action} the issue if saved:false`, function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) { this.$triggeredButton.trigger('click');
expectPendingRequest(req, $btnClose); this.issueStateDeferred.resolve({
req.success({
saved: false saved: false
}); });
this.canCreateBranchDeferred.resolve({
can_create_branch: isIssueInitiallyOpen
}); });
$btnClose.attr('href', INVALID_URL); expectIssueState(isIssueInitiallyOpen);
$btnClose.trigger('click'); expect(this.$triggeredButton).toHaveProp('disabled', false);
expectIssueState(true);
expect($btnClose).toHaveProp('disabled', false);
expectErrorMessage(); expectErrorMessage();
expect($('.issue_counter')).toHaveText(1); expect(this.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
}); });
it('fails to closes an issue with HTTP error', function() { it(`fails to ${action} the issue if HTTP error occurs`, function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) { this.$triggeredButton.trigger('click');
expectPendingRequest(req, $btnClose); this.issueStateDeferred.reject();
req.error(); this.canCreateBranchDeferred.resolve({
can_create_branch: isIssueInitiallyOpen
}); });
$btnClose.attr('href', INVALID_URL); expectIssueState(isIssueInitiallyOpen);
$btnClose.trigger('click'); expect(this.$triggeredButton).toHaveProp('disabled', true);
expectIssueState(true);
expect($btnClose).toHaveProp('disabled', true);
expectErrorMessage(); expectErrorMessage();
expect($('.issue_counter')).toHaveText(1); expect(this.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
}); });
it('updates counter', () => { it('disables the new branch button if Ajax call fails', function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) { this.$triggeredButton.trigger('click');
expectPendingRequest(req, $btnClose); this.issueStateDeferred.reject();
req.success({ this.canCreateBranchDeferred.reject();
id: 34
});
});
expect($('.issue_counter')).toHaveText(1); expectNewBranchButtonState(false, false);
$('.issue_counter').text('1,001');
expect($('.issue_counter').text()).toEqual('1,001');
$btnClose.trigger('click');
expect($('.issue_counter').text()).toEqual('1,000');
});
}); });
describe('reopen issue', function() { it('does not trigger Ajax call if new branch button is missing', function() {
beforeEach(function() { Issue.$btnNewBranch = $();
loadFixtures('issues/closed-issue.html.raw'); this.canCreateBranchDeferred = null;
findElements();
this.issue = new Issue();
expectIssueState(false); this.$triggeredButton.trigger('click');
this.issueStateDeferred.reject();
}); });
it('reopens an issue', function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expectPendingRequest(req, $btnReopen);
req.success({
id: 34
});
});
$btnReopen.trigger('click');
expectIssueState(true);
expect($btnReopen).toHaveProp('disabled', false);
expect($('.issue_counter')).toHaveText(1);
}); });
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment