Commit c3ebc8a1 authored by Mark Florian's avatar Mark Florian

Merge branch '276444-delete-unused-code-from-vue_issue_header-feature-flag-removal' into 'master'

Remove unused issue header and issue blocked-by modal code

See merge request gitlab-org/gitlab!48762
parents 26f69bf9 ceb69b8e
import DropLab from './droplab/drop_lab';
import ISetter from './droplab/plugins/input_setter';
// Todo: Remove this when fixing issue in input_setter plugin
const InputSetter = { ...ISetter };
class CloseReopenReportToggle {
constructor(opts = {}) {
this.dropdownTrigger = opts.dropdownTrigger;
this.dropdownList = opts.dropdownList;
this.button = opts.button;
}
initDroplab() {
this.reopenItem = this.dropdownList.querySelector('.reopen-item');
this.closeItem = this.dropdownList.querySelector('.close-item');
this.droplab = new DropLab();
const config = this.setConfig();
this.droplab.init(this.dropdownTrigger, this.dropdownList, [InputSetter], config);
}
updateButton(isClosed) {
this.toggleButtonType(isClosed);
this.button.blur();
}
toggleButtonType(isClosed) {
const [showItem, hideItem] = this.getButtonTypes(isClosed);
showItem.classList.remove('hidden');
showItem.classList.add('droplab-item-selected');
hideItem.classList.add('hidden');
hideItem.classList.remove('droplab-item-selected');
showItem.click();
}
getButtonTypes(isClosed) {
return isClosed ? [this.reopenItem, this.closeItem] : [this.closeItem, this.reopenItem];
}
setDisable(shouldDisable = true) {
if (shouldDisable) {
this.button.setAttribute('disabled', 'true');
this.dropdownTrigger.setAttribute('disabled', 'true');
} else {
this.button.removeAttribute('disabled');
this.dropdownTrigger.removeAttribute('disabled');
}
}
setConfig() {
const config = {
InputSetter: [
{
input: this.button,
valueAttribute: 'data-text',
inputAttribute: 'data-value',
},
{
input: this.button,
valueAttribute: 'data-text',
inputAttribute: 'title',
},
{
input: this.button,
valueAttribute: 'data-button-class',
inputAttribute: 'class',
},
{
input: this.dropdownTrigger,
valueAttribute: 'data-toggle-class',
inputAttribute: 'class',
},
{
input: this.button,
valueAttribute: 'data-url',
inputAttribute: 'data-endpoint',
},
],
};
return config;
}
}
export default CloseReopenReportToggle;
import CloseReopenReportToggle from '../close_reopen_report_toggle';
function initCloseReopenReport() {
const container = document.querySelector('.js-issuable-close-dropdown');
if (!container) return undefined;
const dropdownTrigger = container.querySelector('.js-issuable-close-toggle');
const dropdownList = container.querySelector('.js-issuable-close-menu');
const button = container.querySelector('.js-issuable-close-button');
const closeReopenReportToggle = new CloseReopenReportToggle({
dropdownTrigger,
dropdownList,
button,
});
closeReopenReportToggle.initDroplab();
return closeReopenReportToggle;
}
const IssuablesHelper = {
initCloseReopenReport,
};
export default IssuablesHelper;
......@@ -112,7 +112,7 @@ export default {
</div>
<div
data-testid="header-actions"
class="detail-page-header-actions js-issuable-actions js-issuable-buttons gl-display-flex gl-display-md-block"
class="detail-page-header-actions gl-display-flex gl-display-md-block"
>
<slot name="header-actions"></slot>
</div>
......
/* eslint-disable consistent-return */
import $ from 'jquery';
import axios from './lib/utils/axios_utils';
import { addDelimiter } from './lib/utils/text_utility';
import { deprecatedCreateFlash as flash } from './flash';
import CreateMergeRequestDropdown from './create_merge_request_dropdown';
import IssuablesHelper from './helpers/issuables_helper';
import { joinPaths } from '~/lib/utils/url_utility';
import { __ } from './locale';
export default class Issue {
constructor() {
if ($('.btn-close, .btn-reopen').length) this.initIssueBtnEventListeners();
if ($('.js-close-blocked-issue-warning').length) this.initIssueWarningBtnEventListener();
if ($('.js-alert-moved-from-service-desk-warning').length) {
const trimmedPathname = window.location.pathname.slice(1);
this.alertMovedFromServiceDeskDismissedKey = joinPaths(
trimmedPathname,
'alert-issue-moved-from-service-desk-dismissed',
);
this.initIssueMovedFromServiceDeskDismissHandler();
Issue.initIssueMovedFromServiceDeskDismissHandler();
}
Issue.$btnNewBranch = $('#new-branch');
Issue.createMrDropdownWrap = document.querySelector('.create-mr-dropdown-wrap');
if (document.querySelector('#related-branches')) {
Issue.initRelatedBranches();
}
this.closeButtons = $('.btn-close');
this.reopenButtons = $('.btn-reopen');
this.initCloseReopenReport();
Issue.createMrDropdownWrap = document.querySelector('.create-mr-dropdown-wrap');
if (Issue.createMrDropdownWrap) {
this.createMergeRequestDropdown = new CreateMergeRequestDropdown(Issue.createMrDropdownWrap);
......@@ -71,7 +52,6 @@ export default class Issue {
isOpenBadge.toggleClass('hidden', isClosed);
$(document).trigger('issuable:change', isClosed);
this.toggleCloseReopenButton(isClosed);
let numProjectIssues = Number(
projectIssuesCounter
......@@ -91,104 +71,16 @@ export default class Issue {
}
}
initIssueBtnEventListeners() {
const issueFailMessage = __('Unable to update this issue at this time.');
$('.report-abuse-link').on('click', e => {
// this is needed because of the implementation of
// the dropdown toggle and Report Abuse needing to be
// linked to another page.
e.stopPropagation();
});
// NOTE: data attribute seems unnecessary but is actually necessary
return $('.js-issuable-buttons[data-action="close-reopen"]').on(
'click',
'.btn-close, .btn-reopen, .btn-close-anyway',
e => {
e.preventDefault();
e.stopImmediatePropagation();
const $button = $(e.currentTarget);
const shouldSubmit = $button.hasClass('btn-comment');
if (shouldSubmit) {
Issue.submitNoteForm($button.closest('form'));
}
const shouldDisplayBlockedWarning = $button.hasClass('btn-issue-blocked');
const warningBanner = $('.js-close-blocked-issue-warning');
if (shouldDisplayBlockedWarning) {
this.toggleWarningAndCloseButton();
} else {
this.disableCloseReopenButton($button);
const url = $button.data('endpoint');
return axios
.put(url)
.then(({ data }) => {
const isClosed = $button.is('.btn-close, .btn-close-anyway');
this.updateTopState(isClosed, data);
if ($button.hasClass('btn-close-anyway')) {
warningBanner.addClass('hidden');
if (this.closeReopenReportToggle)
$('.js-issuable-close-dropdown').removeClass('hidden');
}
})
.catch(() => flash(issueFailMessage))
.then(() => {
this.disableCloseReopenButton($button, false);
});
}
},
);
}
initCloseReopenReport() {
this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport();
if (this.closeButtons) this.closeButtons = this.closeButtons.not('.issuable-close-button');
if (this.reopenButtons) this.reopenButtons = this.reopenButtons.not('.issuable-close-button');
}
disableCloseReopenButton($button, shouldDisable) {
if (this.closeReopenReportToggle) {
this.closeReopenReportToggle.setDisable(shouldDisable);
} else {
$button.prop('disabled', shouldDisable);
}
}
toggleCloseReopenButton(isClosed) {
if (this.closeReopenReportToggle) this.closeReopenReportToggle.updateButton(isClosed);
this.closeButtons.toggleClass('hidden', isClosed);
this.reopenButtons.toggleClass('hidden', !isClosed);
}
toggleWarningAndCloseButton() {
const warningBanner = $('.js-close-blocked-issue-warning');
warningBanner.toggleClass('hidden');
$('.btn-close').toggleClass('hidden');
if (this.closeReopenReportToggle) {
$('.js-issuable-close-dropdown').toggleClass('hidden');
}
}
static initIssueMovedFromServiceDeskDismissHandler() {
const alertMovedFromServiceDeskWarning = $('.js-alert-moved-from-service-desk-warning');
initIssueWarningBtnEventListener() {
return $(document).on(
'click',
'.js-close-blocked-issue-warning .js-cancel-blocked-issue-warning',
e => {
e.preventDefault();
e.stopImmediatePropagation();
this.toggleWarningAndCloseButton();
},
const trimmedPathname = window.location.pathname.slice(1);
const alertMovedFromServiceDeskDismissedKey = joinPaths(
trimmedPathname,
'alert-issue-moved-from-service-desk-dismissed',
);
}
initIssueMovedFromServiceDeskDismissHandler() {
const alertMovedFromServiceDeskWarning = $('.js-alert-moved-from-service-desk-warning');
if (!localStorage.getItem(this.alertMovedFromServiceDeskDismissedKey)) {
if (!localStorage.getItem(alertMovedFromServiceDeskDismissedKey)) {
alertMovedFromServiceDeskWarning.show();
}
......@@ -196,20 +88,13 @@ export default class Issue {
e.preventDefault();
e.stopImmediatePropagation();
alertMovedFromServiceDeskWarning.remove();
localStorage.setItem(this.alertMovedFromServiceDeskDismissedKey, true);
localStorage.setItem(alertMovedFromServiceDeskDismissedKey, true);
});
}
static submitNoteForm(form) {
const noteText = form.find('textarea.js-note-text').val();
if (noteText && noteText.trim().length > 0) {
return form.submit();
}
}
static initRelatedBranches() {
const $container = $('#related-branches');
return axios
axios
.get($container.data('url'))
.then(({ data }) => {
if ('html' in data) {
......
......@@ -887,11 +887,6 @@
}
}
.issuable-close-button,
.issuable-close-toggle {
@include transition(border-color, color);
}
.issuable-close-dropdown {
.dropdown-menu {
min-width: 270px;
......
......@@ -3,7 +3,6 @@
- if issuable.relocation_target
- page_canonical_link issuable.relocation_target.present(current_user: current_user).web_url
= render_if_exists "projects/issues/alert_blocked", issue: issuable, current_user: current_user
= render "projects/issues/alert_moved_from_service_desk", issue: issuable
= render 'shared/issue_type/details_header', issuable: issuable
......
- blocked_by_issues = @issue.blocked_by_issues_for(current_user)
- blocked_by_issues_links = blocked_by_issues.map { |blocking_issue| link_to "\##{blocking_issue.iid}", project_issue_path(blocking_issue.project, blocking_issue), class: 'gl-link' }.join(', ').html_safe
- if @issue.blocked? && @issue.blocked_by_issues_for(current_user).length > 0
.hidden.js-close-blocked-issue-warning.js-issuable-buttons.gl-alert.gl-alert-warning.gl-mt-5{ role: 'alert', data: { 'action': "close-reopen" } }
= sprite_icon('warning', css_class: 'gl-icon gl-alert-icon')
%h4.gl-alert-title
= _('Are you sure you want to close this blocked issue?')
.gl-alert-body
= _('This issue is currently blocked by the following issues: %{issues}.').html_safe % { issues: blocked_by_issues_links }
.gl-alert-actions
%button{ class: "btn btn-close-anyway gl-alert-action btn-warning btn-md gl-button", data: { endpoint: close_issuable_path(@issue) } }
= _("Yes, close issue")
%button.btn.gl-alert-action.btn-warning.btn-md.gl-button.btn-warning-secondary.js-cancel-blocked-issue-warning
= s_('Cancel')
......@@ -27818,9 +27818,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
msgid "This issue is currently blocked by the following issues: %{issues}."
msgstr ""
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
......
import CloseReopenReportToggle from '~/close_reopen_report_toggle';
import DropLab from '~/droplab/drop_lab';
describe('CloseReopenReportToggle', () => {
describe('class constructor', () => {
const dropdownTrigger = {};
const dropdownList = {};
const button = {};
let commentTypeToggle;
beforeEach(() => {
commentTypeToggle = new CloseReopenReportToggle({
dropdownTrigger,
dropdownList,
button,
});
});
it('sets .dropdownTrigger', () => {
expect(commentTypeToggle.dropdownTrigger).toBe(dropdownTrigger);
});
it('sets .dropdownList', () => {
expect(commentTypeToggle.dropdownList).toBe(dropdownList);
});
it('sets .button', () => {
expect(commentTypeToggle.button).toBe(button);
});
});
describe('initDroplab', () => {
let closeReopenReportToggle;
const dropdownList = {
querySelector: jest.fn(),
};
const dropdownTrigger = {};
const button = {};
const reopenItem = {};
const closeItem = {};
const config = {};
beforeEach(() => {
jest.spyOn(DropLab.prototype, 'init').mockImplementation(() => {});
dropdownList.querySelector.mockReturnValueOnce(reopenItem).mockReturnValueOnce(closeItem);
closeReopenReportToggle = new CloseReopenReportToggle({
dropdownTrigger,
dropdownList,
button,
});
jest.spyOn(closeReopenReportToggle, 'setConfig').mockReturnValue(config);
closeReopenReportToggle.initDroplab();
});
it('sets .reopenItem and .closeItem', () => {
expect(dropdownList.querySelector).toHaveBeenCalledWith('.reopen-item');
expect(dropdownList.querySelector).toHaveBeenCalledWith('.close-item');
expect(closeReopenReportToggle.reopenItem).toBe(reopenItem);
expect(closeReopenReportToggle.closeItem).toBe(closeItem);
});
it('sets .droplab', () => {
expect(closeReopenReportToggle.droplab).toEqual(expect.any(Object));
});
it('calls .setConfig', () => {
expect(closeReopenReportToggle.setConfig).toHaveBeenCalled();
});
it('calls droplab.init', () => {
expect(DropLab.prototype.init).toHaveBeenCalledWith(
dropdownTrigger,
dropdownList,
expect.any(Array),
config,
);
});
});
describe('updateButton', () => {
let closeReopenReportToggle;
const dropdownList = {};
const dropdownTrigger = {};
const button = {
blur: jest.fn(),
};
const isClosed = true;
beforeEach(() => {
closeReopenReportToggle = new CloseReopenReportToggle({
dropdownTrigger,
dropdownList,
button,
});
jest.spyOn(closeReopenReportToggle, 'toggleButtonType').mockImplementation(() => {});
closeReopenReportToggle.updateButton(isClosed);
});
it('calls .toggleButtonType', () => {
expect(closeReopenReportToggle.toggleButtonType).toHaveBeenCalledWith(isClosed);
});
it('calls .button.blur', () => {
expect(closeReopenReportToggle.button.blur).toHaveBeenCalled();
});
});
describe('toggleButtonType', () => {
let closeReopenReportToggle;
const dropdownList = {};
const dropdownTrigger = {};
const button = {};
const isClosed = true;
const showItem = {
click: jest.fn(),
};
const hideItem = {};
showItem.classList = {
add: jest.fn(),
remove: jest.fn(),
};
hideItem.classList = {
add: jest.fn(),
remove: jest.fn(),
};
beforeEach(() => {
closeReopenReportToggle = new CloseReopenReportToggle({
dropdownTrigger,
dropdownList,
button,
});
jest.spyOn(closeReopenReportToggle, 'getButtonTypes').mockReturnValue([showItem, hideItem]);
closeReopenReportToggle.toggleButtonType(isClosed);
});
it('calls .getButtonTypes', () => {
expect(closeReopenReportToggle.getButtonTypes).toHaveBeenCalledWith(isClosed);
});
it('removes hide class and add selected class to showItem, opposite for hideItem', () => {
expect(showItem.classList.remove).toHaveBeenCalledWith('hidden');
expect(showItem.classList.add).toHaveBeenCalledWith('droplab-item-selected');
expect(hideItem.classList.add).toHaveBeenCalledWith('hidden');
expect(hideItem.classList.remove).toHaveBeenCalledWith('droplab-item-selected');
});
it('clicks the showItem', () => {
expect(showItem.click).toHaveBeenCalled();
});
});
describe('getButtonTypes', () => {
let closeReopenReportToggle;
const dropdownList = {};
const dropdownTrigger = {};
const button = {};
const reopenItem = {};
const closeItem = {};
beforeEach(() => {
closeReopenReportToggle = new CloseReopenReportToggle({
dropdownTrigger,
dropdownList,
button,
});
closeReopenReportToggle.reopenItem = reopenItem;
closeReopenReportToggle.closeItem = closeItem;
});
it('returns reopenItem, closeItem if isClosed is true', () => {
const buttonTypes = closeReopenReportToggle.getButtonTypes(true);
expect(buttonTypes).toEqual([reopenItem, closeItem]);
});
it('returns closeItem, reopenItem if isClosed is false', () => {
const buttonTypes = closeReopenReportToggle.getButtonTypes(false);
expect(buttonTypes).toEqual([closeItem, reopenItem]);
});
});
describe('setDisable', () => {
let closeReopenReportToggle;
const dropdownList = {};
const dropdownTrigger = {
setAttribute: jest.fn(),
removeAttribute: jest.fn(),
};
const button = {
setAttribute: jest.fn(),
removeAttribute: jest.fn(),
};
beforeEach(() => {
closeReopenReportToggle = new CloseReopenReportToggle({
dropdownTrigger,
dropdownList,
button,
});
});
it('disable .button and .dropdownTrigger if shouldDisable is true', () => {
closeReopenReportToggle.setDisable(true);
expect(button.setAttribute).toHaveBeenCalledWith('disabled', 'true');
expect(dropdownTrigger.setAttribute).toHaveBeenCalledWith('disabled', 'true');
});
it('disable .button and .dropdownTrigger if shouldDisable is undefined', () => {
closeReopenReportToggle.setDisable();
expect(button.setAttribute).toHaveBeenCalledWith('disabled', 'true');
expect(dropdownTrigger.setAttribute).toHaveBeenCalledWith('disabled', 'true');
});
it('enable .button and .dropdownTrigger if shouldDisable is false', () => {
closeReopenReportToggle.setDisable(false);
expect(button.removeAttribute).toHaveBeenCalledWith('disabled');
expect(dropdownTrigger.removeAttribute).toHaveBeenCalledWith('disabled');
});
});
describe('setConfig', () => {
let closeReopenReportToggle;
const dropdownList = {};
const dropdownTrigger = {};
const button = {};
let config;
beforeEach(() => {
closeReopenReportToggle = new CloseReopenReportToggle({
dropdownTrigger,
dropdownList,
button,
});
config = closeReopenReportToggle.setConfig();
});
it('returns a config object', () => {
expect(config).toEqual({
InputSetter: [
{
input: button,
valueAttribute: 'data-text',
inputAttribute: 'data-value',
},
{
input: button,
valueAttribute: 'data-text',
inputAttribute: 'title',
},
{
input: button,
valueAttribute: 'data-button-class',
inputAttribute: 'class',
},
{
input: dropdownTrigger,
valueAttribute: 'data-toggle-class',
inputAttribute: 'class',
},
{
input: button,
valueAttribute: 'data-url',
inputAttribute: 'data-endpoint',
},
],
});
});
});
});
......@@ -40,11 +40,6 @@ RSpec.describe Projects::IssuesController, '(JavaScript fixtures)', type: :contr
render_issue(create(:closed_issue, project: project))
end
it 'issues/issue-with-task-list.html' do
issue = create(:issue, project: project, description: '- [ ] Task List Item')
render_issue(issue)
end
it 'issues/issue_list.html' do
create(:issue, project: project)
......
<div class="description" updated-at="">
<div class="md issue-realtime-trigger-pulse">
<svg
id="mermaid-1587752414912"
width="100%"
xmlns="http://www.w3.org/2000/svg"
style="max-width: 185.35000610351562px;"
viewBox="0 0 185.35000610351562 50.5"
class="mermaid"
>
<g transform="translate(0, 0)">
<g class="output">
<g class="clusters"></g>
<g class="edgePaths"></g>
<g class="edgeLabels"></g>
<g class="nodes">
<g
class="node js-issuable-buttons btn-close clickable"
style="opacity: 1;"
id="A"
transform="translate(92.67500305175781,25.25)"
title="click to PUT"
>
<a
class="js-issuable-buttons btn-close clickable"
href="https://invalid"
rel="noopener"
>
<rect
rx="0"
ry="0"
x="-84.67500305175781"
y="-17.25"
width="169.35000610351562"
height="34.5"
class="label-container"
></rect>
<g class="label" transform="translate(0,0)">
<g transform="translate(-74.67500305175781,-7.25)">
<text style="">
<tspan xml:space="preserve" dy="1em" x="1">Click to send a PUT request</tspan>
</text>
</g>
</g>
</a>
</g>
</g>
</g>
</g>
<text class="source" display="none">
Click to send a PUT request
</text>
</svg>
</div>
<textarea
data-update-url="/h5bp/html5-boilerplate/-/issues/35.json"
dir="auto"
class="hidden js-task-list-field"
></textarea>
<div class="modal-open recaptcha-modal js-recaptcha-modal" style="display: none;">
<div role="dialog" tabindex="-1" class="modal d-block">
<div role="document" class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title float-left">Please solve the reCAPTCHA</h4>
<button type="button" data-dismiss="modal" aria-label="Close" class="close float-right">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div>
<p>We want to be sure it is you, please confirm you are not a robot.</p>
<div></div>
</div>
</div>
<!---->
</div>
</div>
</div>
<div class="modal-backdrop fade show"></div>
</div>
</div>
/* eslint-disable one-var, no-use-before-define */
import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
......@@ -7,23 +5,16 @@ import Issue from '~/issue';
import '~/lib/utils/text_utility';
describe('Issue', () => {
let $boxClosed;
let $boxOpen;
let testContext;
beforeEach(() => {
testContext = {};
});
let $boxClosed, $boxOpen;
preloadFixtures('issues/closed-issue.html');
preloadFixtures('issues/issue-with-task-list.html');
preloadFixtures('issues/open-issue.html');
preloadFixtures('static/issue_with_mermaid_graph.html');
function expectIssueState(isIssueOpen) {
expectVisibility($boxClosed, !isIssueOpen);
expectVisibility($boxOpen, isIssueOpen);
}
function expectVisibility($element, shouldBeVisible) {
if (shouldBeVisible) {
......@@ -33,6 +24,11 @@ describe('Issue', () => {
}
}
function expectIssueState(isIssueOpen) {
expectVisibility($boxClosed, !isIssueOpen);
expectVisibility($boxOpen, isIssueOpen);
}
function findElements() {
$boxClosed = $('div.status-box-issue-closed');
......@@ -93,30 +89,4 @@ describe('Issue', () => {
});
});
});
describe('when not displaying blocked warning', () => {
describe('when clicking a mermaid graph inside an issue description', () => {
let mock;
let spy;
beforeEach(() => {
loadFixtures('static/issue_with_mermaid_graph.html');
mock = new MockAdapter(axios);
spy = jest.spyOn(axios, 'put');
});
afterEach(() => {
mock.restore();
jest.clearAllMocks();
});
it('does not make a PUT request', () => {
Issue.prototype.initIssueBtnEventListeners();
$('svg a.js-issuable-actions').trigger('click');
expect(spy).not.toHaveBeenCalled();
});
});
});
});
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