2018-03-09 15:18:59 -05:00
|
|
|
import $ from 'jquery';
|
2017-08-09 07:57:45 -04:00
|
|
|
import Pikaday from 'pikaday';
|
2019-04-23 15:18:07 -04:00
|
|
|
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
|
2017-10-12 12:31:29 -04:00
|
|
|
import Autosave from './autosave';
|
2017-05-12 04:15:28 -04:00
|
|
|
import UsersSelect from './users_select';
|
2017-06-30 17:37:31 -04:00
|
|
|
import ZenMode from './zen_mode';
|
2018-01-12 07:59:08 -05:00
|
|
|
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
|
2018-10-24 06:51:33 -04:00
|
|
|
import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
|
2019-11-21 04:06:16 -05:00
|
|
|
import { queryToObject, objectToQuery } from './lib/utils/url_utility';
|
2020-11-18 04:09:02 -05:00
|
|
|
import { loadCSSFile } from './lib/utils/css_utils';
|
2019-11-21 04:06:16 -05:00
|
|
|
|
2020-01-15 01:08:54 -05:00
|
|
|
const MR_SOURCE_BRANCH = 'merge_request[source_branch]';
|
|
|
|
const MR_TARGET_BRANCH = 'merge_request[target_branch]';
|
|
|
|
|
2019-11-21 04:06:16 -05:00
|
|
|
function organizeQuery(obj, isFallbackKey = false) {
|
2020-01-15 01:08:54 -05:00
|
|
|
if (!obj[MR_SOURCE_BRANCH] && !obj[MR_TARGET_BRANCH]) {
|
|
|
|
return obj;
|
|
|
|
}
|
2019-11-21 04:06:16 -05:00
|
|
|
|
|
|
|
if (isFallbackKey) {
|
|
|
|
return {
|
2020-01-15 01:08:54 -05:00
|
|
|
[MR_SOURCE_BRANCH]: obj[MR_SOURCE_BRANCH],
|
2019-11-21 04:06:16 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
2020-01-15 01:08:54 -05:00
|
|
|
[MR_SOURCE_BRANCH]: obj[MR_SOURCE_BRANCH],
|
|
|
|
[MR_TARGET_BRANCH]: obj[MR_TARGET_BRANCH],
|
2019-11-21 04:06:16 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function format(searchTerm, isFallbackKey = false) {
|
|
|
|
const queryObject = queryToObject(searchTerm);
|
|
|
|
const organizeQueryObject = organizeQuery(queryObject, isFallbackKey);
|
|
|
|
const formattedQuery = objectToQuery(organizeQueryObject);
|
|
|
|
|
|
|
|
return formattedQuery;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getFallbackKey() {
|
|
|
|
const searchTerm = format(document.location.search, true);
|
|
|
|
return ['autosave', document.location.pathname, searchTerm].join('/');
|
|
|
|
}
|
2017-05-12 04:15:28 -04:00
|
|
|
|
2017-10-26 16:21:55 -04:00
|
|
|
export default class IssuableForm {
|
|
|
|
constructor(form) {
|
|
|
|
this.form = form;
|
|
|
|
this.toggleWip = this.toggleWip.bind(this);
|
|
|
|
this.renderWipExplanation = this.renderWipExplanation.bind(this);
|
|
|
|
this.resetAutosave = this.resetAutosave.bind(this);
|
|
|
|
this.handleSubmit = this.handleSubmit.bind(this);
|
2020-07-17 17:09:23 -04:00
|
|
|
/* eslint-disable @gitlab/require-i18n-strings */
|
|
|
|
this.wipRegex = new RegExp(
|
|
|
|
'^\\s*(' + // Line start, then any amount of leading whitespace
|
2020-12-23 07:10:26 -05:00
|
|
|
'draft\\s-\\s' + // Draft_-_ where "_" are *exactly* one whitespace
|
|
|
|
'|\\[(draft|wip)\\]\\s*' + // [Draft] or [WIP] and any following whitespace
|
|
|
|
'|(draft|wip):\\s*' + // Draft: or WIP: and any following whitespace
|
|
|
|
'|(draft|wip)\\s+' + // Draft_ or WIP_ where "_" is at least one whitespace
|
|
|
|
'|\\(draft\\)\\s*' + // (Draft) and any following whitespace
|
|
|
|
')+' + // At least one repeated match of the preceding parenthetical
|
2020-07-17 17:09:23 -04:00
|
|
|
'\\s*', // Any amount of trailing whitespace
|
|
|
|
'i', // Match any case(s)
|
|
|
|
);
|
|
|
|
/* eslint-enable @gitlab/require-i18n-strings */
|
2017-10-26 16:21:55 -04:00
|
|
|
|
2018-10-24 07:30:39 -04:00
|
|
|
this.gfmAutoComplete = new GfmAutoComplete(
|
|
|
|
gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources,
|
|
|
|
).setup();
|
|
|
|
this.usersSelect = new UsersSelect();
|
2020-09-14 08:09:34 -04:00
|
|
|
this.reviewersSelect = new UsersSelect(undefined, '.js-reviewer-search');
|
2018-10-24 07:30:39 -04:00
|
|
|
this.zenMode = new ZenMode();
|
2017-10-26 16:21:55 -04:00
|
|
|
|
|
|
|
this.titleField = this.form.find('input[name*="[title]"]');
|
|
|
|
this.descriptionField = this.form.find('textarea[name*="[description]"]');
|
|
|
|
if (!(this.titleField.length && this.descriptionField.length)) {
|
|
|
|
return;
|
2016-07-24 16:45:11 -04:00
|
|
|
}
|
|
|
|
|
2017-10-26 16:21:55 -04:00
|
|
|
this.initAutosave();
|
2018-05-29 20:51:30 -04:00
|
|
|
this.form.on('submit', this.handleSubmit);
|
2017-10-26 16:21:55 -04:00
|
|
|
this.form.on('click', '.btn-cancel', this.resetAutosave);
|
|
|
|
this.initWip();
|
|
|
|
|
|
|
|
const $issuableDueDate = $('#issuable-due-date');
|
|
|
|
|
|
|
|
if ($issuableDueDate.length) {
|
|
|
|
const calendar = new Pikaday({
|
|
|
|
field: $issuableDueDate.get(0),
|
|
|
|
theme: 'gitlab-theme animate-picker',
|
|
|
|
format: 'yyyy-mm-dd',
|
|
|
|
container: $issuableDueDate.parent().get(0),
|
2020-12-23 19:10:25 -05:00
|
|
|
parse: (dateString) => parsePikadayDate(dateString),
|
|
|
|
toString: (date) => pikadayToString(date),
|
|
|
|
onSelect: (dateText) => $issuableDueDate.val(calendar.toString(dateText)),
|
2018-11-06 16:16:49 -05:00
|
|
|
firstDay: gon.first_day_of_week,
|
2017-10-26 16:21:55 -04:00
|
|
|
});
|
|
|
|
calendar.setDate(parsePikadayDate($issuableDueDate.val()));
|
|
|
|
}
|
2018-01-12 07:25:18 -05:00
|
|
|
|
|
|
|
this.$targetBranchSelect = $('.js-target-branch-select', this.form);
|
|
|
|
|
|
|
|
if (this.$targetBranchSelect.length) {
|
|
|
|
this.initTargetBranchDropdown();
|
|
|
|
}
|
2017-10-26 16:21:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
initAutosave() {
|
2020-01-15 01:08:54 -05:00
|
|
|
const { search } = document.location;
|
|
|
|
const searchTerm = format(search);
|
2019-11-21 04:06:16 -05:00
|
|
|
const fallbackKey = getFallbackKey();
|
|
|
|
|
|
|
|
this.autosave = new Autosave(
|
|
|
|
this.titleField,
|
|
|
|
[document.location.pathname, searchTerm, 'title'],
|
|
|
|
`${fallbackKey}=title`,
|
|
|
|
);
|
|
|
|
|
|
|
|
return new Autosave(
|
|
|
|
this.descriptionField,
|
|
|
|
[document.location.pathname, searchTerm, 'description'],
|
|
|
|
`${fallbackKey}=description`,
|
|
|
|
);
|
2017-10-26 16:21:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
handleSubmit() {
|
|
|
|
return this.resetAutosave();
|
|
|
|
}
|
|
|
|
|
|
|
|
resetAutosave() {
|
|
|
|
this.titleField.data('autosave').reset();
|
|
|
|
return this.descriptionField.data('autosave').reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
initWip() {
|
|
|
|
this.$wipExplanation = this.form.find('.js-wip-explanation');
|
|
|
|
this.$noWipExplanation = this.form.find('.js-no-wip-explanation');
|
|
|
|
if (!(this.$wipExplanation.length && this.$noWipExplanation.length)) {
|
2018-10-24 07:30:39 -04:00
|
|
|
return undefined;
|
2017-10-26 16:21:55 -04:00
|
|
|
}
|
|
|
|
this.form.on('click', '.js-toggle-wip', this.toggleWip);
|
|
|
|
this.titleField.on('keyup blur', this.renderWipExplanation);
|
|
|
|
return this.renderWipExplanation();
|
|
|
|
}
|
|
|
|
|
|
|
|
workInProgress() {
|
|
|
|
return this.wipRegex.test(this.titleField.val());
|
|
|
|
}
|
2020-07-17 17:09:23 -04:00
|
|
|
titlePrefixContainsDraft() {
|
|
|
|
const prefix = this.titleField.val().match(this.wipRegex);
|
|
|
|
|
|
|
|
return prefix && prefix[0].match(/draft/i);
|
|
|
|
}
|
2017-10-26 16:21:55 -04:00
|
|
|
|
|
|
|
renderWipExplanation() {
|
|
|
|
if (this.workInProgress()) {
|
2020-07-17 17:09:23 -04:00
|
|
|
// These strings are not "translatable" (the code is hard-coded to look for them)
|
|
|
|
this.$wipExplanation.find('code')[0].textContent = this.titlePrefixContainsDraft()
|
|
|
|
? 'Draft' /* eslint-disable-line @gitlab/require-i18n-strings */
|
|
|
|
: 'WIP';
|
2017-10-26 16:21:55 -04:00
|
|
|
this.$wipExplanation.show();
|
|
|
|
return this.$noWipExplanation.hide();
|
|
|
|
}
|
2018-10-24 07:30:39 -04:00
|
|
|
this.$wipExplanation.hide();
|
|
|
|
return this.$noWipExplanation.show();
|
2017-10-26 16:21:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
toggleWip(event) {
|
|
|
|
event.preventDefault();
|
|
|
|
if (this.workInProgress()) {
|
|
|
|
this.removeWip();
|
|
|
|
} else {
|
|
|
|
this.addWip();
|
|
|
|
}
|
|
|
|
return this.renderWipExplanation();
|
|
|
|
}
|
|
|
|
|
|
|
|
removeWip() {
|
|
|
|
return this.titleField.val(this.titleField.val().replace(this.wipRegex, ''));
|
|
|
|
}
|
|
|
|
|
|
|
|
addWip() {
|
2020-07-17 17:09:23 -04:00
|
|
|
this.titleField.val(`Draft: ${this.titleField.val()}`);
|
2017-10-26 16:21:55 -04:00
|
|
|
}
|
2018-01-12 07:25:18 -05:00
|
|
|
|
|
|
|
initTargetBranchDropdown() {
|
2019-01-29 04:35:53 -05:00
|
|
|
import(/* webpackChunkName: 'select2' */ 'select2/select2')
|
|
|
|
.then(() => {
|
2020-11-18 04:09:02 -05:00
|
|
|
// eslint-disable-next-line promise/no-nesting
|
|
|
|
loadCSSFile(gon.select2_css_path)
|
|
|
|
.then(() => {
|
|
|
|
this.$targetBranchSelect.select2({
|
|
|
|
...AutoWidthDropdownSelect.selectOptions('js-target-branch-select'),
|
|
|
|
ajax: {
|
|
|
|
url: this.$targetBranchSelect.data('endpoint'),
|
|
|
|
dataType: 'JSON',
|
|
|
|
quietMillis: 250,
|
|
|
|
data(search) {
|
|
|
|
return {
|
|
|
|
search,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
results(data) {
|
|
|
|
return {
|
|
|
|
// `data` keys are translated so we can't just access them with a string based key
|
2020-12-23 19:10:25 -05:00
|
|
|
results: data[Object.keys(data)[0]].map((name) => ({
|
2020-11-18 04:09:02 -05:00
|
|
|
id: name,
|
|
|
|
text: name,
|
|
|
|
})),
|
|
|
|
};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
initSelection(el, callback) {
|
|
|
|
const val = el.val();
|
|
|
|
|
|
|
|
callback({
|
|
|
|
id: val,
|
|
|
|
text: val,
|
|
|
|
});
|
|
|
|
},
|
2019-01-29 04:35:53 -05:00
|
|
|
});
|
2020-11-18 04:09:02 -05:00
|
|
|
})
|
|
|
|
.catch(() => {});
|
2019-01-29 04:35:53 -05:00
|
|
|
})
|
|
|
|
.catch(() => {});
|
2018-01-12 07:25:18 -05:00
|
|
|
}
|
2017-10-26 16:21:55 -04:00
|
|
|
}
|