Commit 0b398bcf authored by Thomas Randolph's avatar Thomas Randolph Committed by Enrique Alcántara

Add Draft to WIP MRs

Also, default to Draft when the dynamic link is clicked
parent 12a419dc
......@@ -48,7 +48,19 @@ export default class IssuableForm {
this.renderWipExplanation = this.renderWipExplanation.bind(this);
this.resetAutosave = this.resetAutosave.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.wipRegex = /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i;
/* eslint-disable @gitlab/require-i18n-strings */
this.wipRegex = new RegExp(
'^\\s*(' + // Line start, then any amount of leading whitespace
'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
'\\s*', // Any amount of trailing whitespace
'i', // Match any case(s)
);
/* eslint-enable @gitlab/require-i18n-strings */
this.gfmAutoComplete = new GfmAutoComplete(
gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources,
......@@ -131,9 +143,18 @@ export default class IssuableForm {
workInProgress() {
return this.wipRegex.test(this.titleField.val());
}
titlePrefixContainsDraft() {
const prefix = this.titleField.val().match(this.wipRegex);
return prefix && prefix[0].match(/draft/i);
}
renderWipExplanation() {
if (this.workInProgress()) {
// 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';
this.$wipExplanation.show();
return this.$noWipExplanation.hide();
}
......@@ -156,7 +177,7 @@ export default class IssuableForm {
}
addWip() {
this.titleField.val(`WIP: ${this.titleField.val()}`);
this.titleField.val(`Draft: ${this.titleField.val()}`);
}
initTargetBranchDropdown() {
......
<script>
import $ from 'jquery';
import { GlDeprecatedButton } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import createFlash from '~/flash';
import StatusIcon from '../mr_widget_status_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
......@@ -11,7 +11,7 @@ export default {
name: 'WorkInProgress',
components: {
StatusIcon,
GlDeprecatedButton,
GlButton,
},
directives: {
tooltip,
......@@ -25,13 +25,6 @@ export default {
isMakingRequest: false,
};
},
computed: {
wipInfoTooltip() {
return s__(
'mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged',
);
},
},
methods: {
handleRemoveWIP() {
this.isMakingRequest = true;
......@@ -55,28 +48,25 @@ export default {
<template>
<div class="mr-widget-body media">
<status-icon :show-disabled-button="Boolean(mr.removeWIPPath)" status="warning" />
<div class="media-body space-children">
<span class="bold">
{{ __('This is a Work in Progress') }}
<i
v-tooltip
class="fa fa-question-circle"
:title="wipInfoTooltip"
:aria-label="wipInfoTooltip"
>
</i>
</span>
<gl-deprecated-button
<div class="media-body">
<div class="gl-ml-3 float-left">
<span class="gl-font-weight-bold">
{{ __('This merge request is still a work in progress.') }}
</span>
<span class="gl-display-block text-muted">{{
__("Draft merge requests can't be merged.")
}}</span>
</div>
<gl-button
v-if="mr.removeWIPPath"
size="sm"
variant="default"
size="small"
:disabled="isMakingRequest"
:loading="isMakingRequest"
class="js-remove-wip"
class="js-remove-wip gl-ml-3"
@click="handleRemoveWIP"
>
{{ s__('mrWidget|Resolve WIP status') }}
</gl-deprecated-button>
{{ s__('mrWidget|Mark as ready') }}
</gl-button>
</div>
</div>
</template>
......@@ -3,6 +3,13 @@
- form = local_assigns.fetch(:form)
- no_issuable_templates = issuable_templates(issuable).empty?
- div_class = no_issuable_templates ? 'col-sm-10' : 'col-sm-7 col-lg-8'
- toggle_wip_link_start = '<a href="" class="js-toggle-wip">'
- toggle_wip_link_end = '</a>'
- draft_snippet = '<code>Draft:</code>'.html_safe
- wip_snippet = '<code>WIP:</code>'.html_safe
- draft_or_wip_snippet = '<code>Draft/WIP</code>'.html_safe
- add_wip_text = (_('%{link_start}Start the title with %{draft_snippet} or %{wip_snippet}%{link_end} to prevent a merge request that is a work in progress from being merged before it\'s ready.') % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: draft_snippet, wip_snippet: wip_snippet } ).html_safe
- remove_wip_text = (_('%{link_start}Remove the %{draft_or_wip_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it\'s ready.' ) % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_or_wip_snippet: draft_or_wip_snippet } ).html_safe
%div{ class: div_class }
= form.text_field :title, required: true, maxlength: 255, autofocus: true,
......@@ -11,23 +18,12 @@
- if issuable.respond_to?(:work_in_progress?)
.form-text.text-muted
.js-wip-explanation
%a.js-toggle-wip{ href: '' }
Remove the
%code WIP:
prefix from the title
to allow this
%strong Work In Progress
merge request to be merged when it's ready.
= remove_wip_text
.js-no-wip-explanation
- if has_wip_commits
It looks like you have some WIP commits in this branch.
= _('It looks like you have some draft commits in this branch.')
%br
%a.js-toggle-wip{ href: '' }
Start the title with
%code WIP:
to prevent a
%strong Work In Progress
merge request from being merged before it's ready.
= add_wip_text
- if no_issuable_templates && can?(current_user, :push_code, issuable.project)
= render 'shared/issuable/form/default_templates'
---
title: Add Draft to WIP for work in progress merge requests
merge_request: 36666
author:
type: added
......@@ -474,6 +474,12 @@ msgstr ""
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
msgid "%{link_start}Remove the %{draft_or_wip_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it's ready."
msgstr ""
msgid "%{link_start}Start the title with %{draft_snippet} or %{wip_snippet}%{link_end} to prevent a merge request that is a work in progress from being merged before it's ready."
msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
......@@ -8500,6 +8506,9 @@ msgstr ""
msgid "Downvotes"
msgstr ""
msgid "Draft merge requests can't be merged."
msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} Designs to attach"
msgstr ""
......@@ -13141,6 +13150,9 @@ msgstr ""
msgid "Issue|Title"
msgstr ""
msgid "It looks like you have some draft commits in this branch."
msgstr ""
msgid "It must have a header row and at least two columns: the first column is the issue title and the second column is the issue description. The separator is automatically detected."
msgstr ""
......@@ -24155,9 +24167,6 @@ msgstr ""
msgid "This is a Premium feature"
msgstr ""
msgid "This is a Work in Progress"
msgstr ""
msgid "This is a confidential %{noteableTypeText}."
msgstr ""
......@@ -24311,6 +24320,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
msgid "This merge request is still a work in progress."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
msgstr ""
......@@ -28412,6 +28424,9 @@ msgstr ""
msgid "mrWidget|Loading deployment statistics"
msgstr ""
msgid "mrWidget|Mark as ready"
msgstr ""
msgid "mrWidget|Mentions"
msgstr ""
......@@ -28469,9 +28484,6 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
msgid "mrWidget|Resolve WIP status"
msgstr ""
msgid "mrWidget|Resolve conflicts"
msgstr ""
......@@ -28550,9 +28562,6 @@ msgstr ""
msgid "mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd} by simply adding a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust."
msgstr ""
msgid "mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged"
msgstr ""
msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
msgstr ""
......
......@@ -32,9 +32,9 @@ RSpec.describe 'Merge request > User resolves Work in Progress', :js do
it 'retains merge request data after clicking Resolve WIP status' do
expect(page.find('.ci-widget-content')).to have_content("Pipeline ##{pipeline.id}")
expect(page).to have_content "This is a Work in Progress"
expect(page).to have_content "This merge request is still a work in progress."
click_button('Resolve WIP status')
click_button('Mark as ready')
wait_for_requests
......@@ -42,7 +42,7 @@ RSpec.describe 'Merge request > User resolves Work in Progress', :js do
# merge request widget refreshes, which masks missing elements
# that should already be present.
expect(page.find('.ci-widget-content', wait: 0)).to have_content("Pipeline ##{pipeline.id}")
expect(page).not_to have_content('This is a Work in Progress')
expect(page).not_to have_content('This merge request is still a work in progress.')
end
end
end
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Merge request > User sees WIP help message' do
RSpec.describe 'Merge request > User sees draft help message' do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
......@@ -11,8 +11,8 @@ RSpec.describe 'Merge request > User sees WIP help message' do
sign_in(user)
end
context 'with WIP commits' do
it 'shows a specific WIP hint' do
context 'with draft commits' do
it 'shows a specific draft hint' do
visit project_new_merge_request_path(
project,
merge_request: {
......@@ -24,14 +24,14 @@ RSpec.describe 'Merge request > User sees WIP help message' do
within_wip_explanation do
expect(page).to have_text(
'It looks like you have some WIP commits in this branch'
'It looks like you have some draft commits in this branch'
)
end
end
end
context 'without WIP commits' do
it 'shows the regular WIP message' do
context 'without draft commits' do
it 'shows the regular draft message' do
visit project_new_merge_request_path(
project,
merge_request: {
......@@ -43,11 +43,11 @@ RSpec.describe 'Merge request > User sees WIP help message' do
within_wip_explanation do
expect(page).not_to have_text(
'It looks like you have some WIP commits in this branch'
'It looks like you have some draft commits in this branch'
)
expect(page).to have_text(
"Start the title with WIP: to prevent a Work In Progress merge \
request from being merged before it's ready"
"Start the title with Draft: or WIP: to prevent a merge request that is a \
work in progress from being merged before it's ready."
)
end
end
......
import $ from 'jquery';
import IssuableForm from '~/issuable_form';
function createIssuable() {
const instance = new IssuableForm($(document.createElement('form')));
instance.titleField = $(document.createElement('input'));
return instance;
}
describe('IssuableForm', () => {
let instance;
beforeEach(() => {
instance = createIssuable();
});
describe('removeWip', () => {
it.each`
prefix
${'wip '}
${' wIP: '}
${'[WIp] '}
${'wIP:'}
${' [WIp]'}
${'drAft '}
${'draFT: '}
${' [DRaft] '}
${'drAft:'}
${'[draFT]'}
${' dRaFt - '}
${'dRaFt - '}
${'(draft) '}
${' (DrafT)'}
${'wip wip: [wip] draft draft - draft: [draft] (draft)'}
`('removes "$prefix" from the beginning of the title', ({ prefix }) => {
instance.titleField.val(`${prefix}The Issuable's Title Value`);
instance.removeWip();
expect(instance.titleField.val()).toBe("The Issuable's Title Value");
});
});
describe('addWip', () => {
it("properly adds the work in progress prefix to the Issuable's title", () => {
instance.titleField.val("The Issuable's Title Value");
instance.addWip();
expect(instance.titleField.val()).toBe("Draft: The Issuable's Title Value");
});
});
});
......@@ -84,11 +84,11 @@ describe('Wip', () => {
it('should have correct elements', () => {
expect(el.classList.contains('mr-widget-body')).toBeTruthy();
expect(el.innerText).toContain('This is a Work in Progress');
expect(el.innerText).toContain('This merge request is still a work in progress.');
expect(el.querySelector('button').getAttribute('disabled')).toBeTruthy();
expect(el.querySelector('button').innerText).toContain('Merge');
expect(el.querySelector('.js-remove-wip').innerText.replace(/\s\s+/g, ' ')).toContain(
'Resolve WIP status',
'Mark as ready',
);
});
......
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