Commit 89f0db81 authored by Phil Hughes's avatar Phil Hughes

Improve draft note header design

Moves the pending badge into the header.
Enables the toggle resolve button to toggle the resolve status
of a draft note.

Closes https://gitlab.com/gitlab-org/gitlab-ee/issues/7905
parent 1f5d50b1
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui'; import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import resolvedStatusMixin from 'ee/batch_comments/mixins/resolved_status';
export default { export default {
name: 'NoteActions', name: 'NoteActions',
...@@ -12,6 +13,7 @@ export default { ...@@ -12,6 +13,7 @@ export default {
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
mixins: [resolvedStatusMixin],
props: { props: {
authorId: { authorId: {
type: Number, type: Number,
...@@ -93,6 +95,8 @@ export default { ...@@ -93,6 +95,8 @@ export default {
return this.getUserDataByProp('id'); return this.getUserDataByProp('id');
}, },
resolveButtonTitle() { resolveButtonTitle() {
if (this.discussionId) return this.resolvedStatusMessage;
let title = 'Mark as resolved'; let title = 'Mark as resolved';
if (this.resolvedBy) { if (this.resolvedBy) {
...@@ -113,6 +117,7 @@ export default { ...@@ -113,6 +117,7 @@ export default {
this.$emit('handleResolve'); this.$emit('handleResolve');
}, },
}, },
showStaysResolved: true,
}; };
</script> </script>
......
<script> <script>
import { mapGetters } from 'vuex';
import $ from 'jquery'; import $ from 'jquery';
import noteEditedText from './note_edited_text.vue'; import noteEditedText from './note_edited_text.vue';
import noteAwardsList from './note_awards_list.vue'; import noteAwardsList from './note_awards_list.vue';
...@@ -30,9 +31,15 @@ export default { ...@@ -30,9 +31,15 @@ export default {
}, },
}, },
computed: { computed: {
...mapGetters(['getDiscussion']),
noteBody() { noteBody() {
return this.note.note; return this.note.note;
}, },
discussion() {
if (!this.note.isDraft) return {};
return this.getDiscussion(this.note.discussion_id);
},
}, },
mounted() { mounted() {
this.renderGFM(); this.renderGFM();
...@@ -56,8 +63,8 @@ export default { ...@@ -56,8 +63,8 @@ export default {
renderGFM() { renderGFM() {
$(this.$refs['note-body']).renderGFM(); $(this.$refs['note-body']).renderGFM();
}, },
handleFormUpdate(note, parentElement, callback) { handleFormUpdate(note, parentElement, callback, resolveDiscussion) {
this.$emit('handleFormUpdate', note, parentElement, callback); this.$emit('handleFormUpdate', note, parentElement, callback, resolveDiscussion);
}, },
formCancelHandler(shouldConfirm, isDirty) { formCancelHandler(shouldConfirm, isDirty) {
this.$emit('cancelForm', shouldConfirm, isDirty); this.$emit('cancelForm', shouldConfirm, isDirty);
...@@ -76,6 +83,8 @@ export default { ...@@ -76,6 +83,8 @@ export default {
:note-body="noteBody" :note-body="noteBody"
:note-id="note.id" :note-id="note.id"
:markdown-version="note.cached_markdown_version" :markdown-version="note.cached_markdown_version"
:discussion="discussion"
:resolve-discussion="note.resolve_discussion"
@handleFormUpdate="handleFormUpdate" @handleFormUpdate="handleFormUpdate"
@cancelForm="formCancelHandler" @cancelForm="formCancelHandler"
/> />
......
...@@ -51,14 +51,19 @@ export default { ...@@ -51,14 +51,19 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
resolveDiscussion: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
updatedNoteBody: this.noteBody, updatedNoteBody: this.noteBody,
conflictWhileEditing: false, conflictWhileEditing: false,
isSubmitting: false, isSubmitting: false,
isResolving: false, isResolving: this.resolveDiscussion,
isUnresolving: false, isUnresolving: !this.resolveDiscussion,
resolveAsThread: true, resolveAsThread: true,
}; };
}, },
...@@ -122,13 +127,19 @@ export default { ...@@ -122,13 +127,19 @@ export default {
const beforeSubmitDiscussionState = this.discussionResolved; const beforeSubmitDiscussionState = this.discussionResolved;
this.isSubmitting = true; this.isSubmitting = true;
this.$emit('handleFormUpdate', this.updatedNoteBody, this.$refs.editNoteForm, () => { this.$emit(
this.isSubmitting = false; 'handleFormUpdate',
this.updatedNoteBody,
this.$refs.editNoteForm,
() => {
this.isSubmitting = false;
if (this.shouldToggleResolved(shouldResolve, beforeSubmitDiscussionState)) { if (this.shouldToggleResolved(shouldResolve, beforeSubmitDiscussionState)) {
this.resolveHandler(beforeSubmitDiscussionState); this.resolveHandler(beforeSubmitDiscussionState);
} }
}); },
this.discussionResolved ? !this.isUnresolving : this.isResolving,
);
}, },
editMyLastNote() { editMyLastNote() {
if (this.updatedNoteBody === '') { if (this.updatedNoteBody === '') {
...@@ -153,7 +164,7 @@ export default { ...@@ -153,7 +164,7 @@ export default {
<div ref="editNoteForm" class="note-edit-form current-note-edit-form js-discussion-note-form"> <div ref="editNoteForm" class="note-edit-form current-note-edit-form js-discussion-note-form">
<div v-if="conflictWhileEditing" class="js-conflict-edit-warning alert alert-danger"> <div v-if="conflictWhileEditing" class="js-conflict-edit-warning alert alert-danger">
This comment has changed since you started editing, please review the This comment has changed since you started editing, please review the
<a :href="noteHash" target="_blank" rel="noopener noreferrer"> updated comment </a> to ensure <a :href="noteHash" target="_blank" rel="noopener noreferrer">updated comment</a> to ensure
information is not lost. information is not lost.
</div> </div>
<div class="flash-container timeline-content"></div> <div class="flash-container timeline-content"></div>
...@@ -178,71 +189,63 @@ export default { ...@@ -178,71 +189,63 @@ export default {
v-model="updatedNoteBody" v-model="updatedNoteBody"
:data-supports-quick-actions="!isEditing" :data-supports-quick-actions="!isEditing"
name="note[note]" name="note[note]"
class="note-textarea js-gfm-input js-note-text class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input"
js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input"
aria-label="Description" aria-label="Description"
placeholder="Write a comment or drag your files here…" placeholder="Write a comment or drag your files here…"
@keydown.meta.enter="handleUpdate();" @keydown.meta.enter="handleUpdate();"
@keydown.ctrl.enter="handleUpdate();" @keydown.ctrl.enter="handleUpdate();"
@keydown.up="editMyLastNote();" @keydown.up="editMyLastNote();"
@keydown.esc="cancelHandler(true);" @keydown.esc="cancelHandler(true);"
> ></textarea>
</textarea>
</markdown-field> </markdown-field>
<div class="note-form-actions clearfix"> <div class="note-form-actions clearfix">
<template v-if="showBatchCommentsActions"> <p v-if="(discussion && discussion.id) || isDraft">
<p v-if="discussion && discussion.id"> <label>
<label> <template v-if="discussionResolved">
<template v-if="discussionResolved"> <input
<input v-model="isUnresolving"
v-model="isUnresolving" type="checkbox"
type="checkbox" class="qa-unresolve-review-discussion"
class="qa-unresolve-review-discussion" />
/> {{ __('Unresolve discussion') }}
{{ __('Unresolve discussion') }} </template>
</template> <template v-else>
<template v-else> <input v-model="isResolving" type="checkbox" class="qa-resolve-review-discussion" />
<input v-model="isResolving" type="checkbox" class="qa-resolve-review-discussion" /> {{ __('Resolve discussion') }}
{{ __('Resolve discussion') }} </template>
</template> </label>
</label> </p>
</p> <div v-if="showBatchCommentsActions">
<div> <button
<button :disabled="isDisabled"
:disabled="isDisabled" type="button"
type="button" class="btn btn-success qa-start-review"
class="btn btn-success qa-start-review" @click="handleAddToReview"
@click="handleAddToReview();" >
> <template v-if="hasDrafts">{{ __('Add to review') }}</template>
<template v-if="hasDrafts"> <template v-else>{{ __('Start a review') }}</template>
{{ __('Add to review') }} </button>
</template> <button
<template v-else> :disabled="isDisabled"
{{ __('Start a review') }} type="button"
</template> class="btn qa-comment-now"
</button> @click="handleUpdate();"
<button >
:disabled="isDisabled" {{ __('Add comment now') }}
type="button" </button>
class="btn qa-comment-now" <button
@click="handleUpdate();" class="btn btn-cancel note-edit-cancel js-close-discussion-note-form"
> type="button"
{{ __('Add comment now') }} @click="cancelHandler();"
</button> >
<button {{ __('Cancel') }}
class="btn btn-cancel note-edit-cancel js-close-discussion-note-form" </button>
type="button" </div>
@click="cancelHandler();"
>
{{ __('Cancel') }}
</button>
</div>
</template>
<template v-else> <template v-else>
<button <button
:disabled="isDisabled" :disabled="isDisabled"
type="button" type="button"
class="js-vue-issue-save btn btn-success js-comment-button " class="js-vue-issue-save btn btn-success js-comment-button"
@click="handleUpdate();" @click="handleUpdate();"
> >
{{ saveButtonTitle }} {{ saveButtonTitle }}
......
...@@ -69,24 +69,23 @@ export default { ...@@ -69,24 +69,23 @@ export default {
type="button" type="button"
@click="handleToggle" @click="handleToggle"
> >
<i :class="toggleChevronClass" class="fa" aria-hidden="true"> </i> <i :class="toggleChevronClass" class="fa" aria-hidden="true"></i>
{{ __('Toggle discussion') }} {{ __('Toggle discussion') }}
</button> </button>
</div> </div>
<a v-if="hasAuthor" v-once :href="author.path"> <a v-if="hasAuthor" v-once :href="author.path">
<slot name="note-header-info"></slot>
<span class="note-header-author-name">{{ author.name }}</span> <span class="note-header-author-name">{{ author.name }}</span>
<span v-if="author.status_tooltip_html" v-html="author.status_tooltip_html"></span> <span v-if="author.status_tooltip_html" v-html="author.status_tooltip_html"></span>
<span class="note-headline-light"> @{{ author.username }} </span> <span class="note-headline-light">@{{ author.username }}</span>
</a> </a>
<span v-else> {{ __('A deleted user') }} </span> <span v-else>{{ __('A deleted user') }}</span>
<span class="note-headline-light"> <span class="note-headline-light">
<span class="note-headline-meta"> <span class="note-headline-meta">
<span class="system-note-message"> <slot></slot> </span> <span class="system-note-message"> <slot></slot> </span>
<template v-if="createdAt"> <template v-if="createdAt">
<span class="system-note-separator"> <span class="system-note-separator">
<template v-if="actionText"> <template v-if="actionText">{{ actionText }}</template>
{{ actionText }}
</template>
</span> </span>
<a <a
:href="noteTimestampLink" :href="noteTimestampLink"
...@@ -100,8 +99,7 @@ export default { ...@@ -100,8 +99,7 @@ export default {
class="fa fa-spinner fa-spin editing-spinner" class="fa fa-spinner fa-spin editing-spinner"
aria-label="Comment is being updated" aria-label="Comment is being updated"
aria-hidden="true" aria-hidden="true"
> ></i>
</i>
</span> </span>
</span> </span>
</div> </div>
......
...@@ -111,10 +111,11 @@ export default { ...@@ -111,10 +111,11 @@ export default {
this.$refs.noteBody.resetAutoSave(); this.$refs.noteBody.resetAutoSave();
this.$emit('updateSuccess'); this.$emit('updateSuccess');
}, },
formUpdateHandler(noteText, parentElement, callback) { formUpdateHandler(noteText, parentElement, callback, resolveDiscussion) {
this.$emit('handleUpdateNote', { this.$emit('handleUpdateNote', {
note: this.note, note: this.note,
noteText, noteText,
resolveDiscussion,
callback: () => this.updateSuccess(), callback: () => this.updateSuccess(),
}); });
...@@ -198,7 +199,9 @@ export default { ...@@ -198,7 +199,9 @@ export default {
:created-at="note.created_at" :created-at="note.created_at"
:note-id="note.id" :note-id="note.id"
action-text="commented" action-text="commented"
/> >
<slot slot="note-header-info" name="note-header-info"> </slot>
</note-header>
<note-actions <note-actions
:author-id="author.id" :author-id="author.id"
:note-id="note.id" :note-id="note.id"
...@@ -208,12 +211,16 @@ export default { ...@@ -208,12 +211,16 @@ export default {
:can-award-emoji="note.current_user.can_award_emoji" :can-award-emoji="note.current_user.can_award_emoji"
:can-delete="note.current_user.can_edit" :can-delete="note.current_user.can_edit"
:can-report-as-abuse="canReportAsAbuse" :can-report-as-abuse="canReportAsAbuse"
:can-resolve="note.current_user.can_resolve" :can-resolve="
note.current_user.can_resolve || (note.isDraft && note.discussion_id !== null)
"
:report-abuse-path="note.report_abuse_path" :report-abuse-path="note.report_abuse_path"
:resolvable="note.resolvable" :resolvable="note.resolvable || note.isDraft"
:is-resolved="note.resolved" :is-resolved="note.resolved || note.resolve_discussion"
:is-resolving="isResolving" :is-resolving="isResolving"
:resolved-by="note.resolved_by" :resolved-by="note.resolved_by"
:discussion-id="note.isDraft && note.discussion_id"
:resolve-discussion="note.isDraft && note.resolve_discussion"
@handleEdit="editHandler" @handleEdit="editHandler"
@handleDelete="deleteHandler" @handleDelete="deleteHandler"
@handleResolve="resolveHandler" @handleResolve="resolveHandler"
......
...@@ -31,12 +31,16 @@ export default { ...@@ -31,12 +31,16 @@ export default {
}, },
methods: { methods: {
resolveHandler(resolvedState = false) { resolveHandler(resolvedState = false) {
if (this.note && this.note.isDraft) {
return this.$emit('toggleResolveStatus');
}
this.isResolving = true; this.isResolving = true;
const isResolved = this.discussionResolved || resolvedState; const isResolved = this.discussionResolved || resolvedState;
const discussion = this.resolveAsThread; const discussion = this.resolveAsThread;
const endpoint = discussion ? this.discussion.resolve_path : `${this.note.path}/resolve`; const endpoint = discussion ? this.discussion.resolve_path : `${this.note.path}/resolve`;
this.toggleResolveNote({ endpoint, isResolved, discussion }) return this.toggleResolveNote({ endpoint, isResolved, discussion })
.then(() => { .then(() => {
this.isResolving = false; this.isResolving = false;
}) })
......
...@@ -3,7 +3,6 @@ import { mapActions, mapGetters, mapState } from 'vuex'; ...@@ -3,7 +3,6 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import NoteableNote from '~/notes/components/noteable_note.vue'; import NoteableNote from '~/notes/components/noteable_note.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import resolvedStatusMixin from '../mixins/resolved_status';
import PublishButton from './publish_button.vue'; import PublishButton from './publish_button.vue';
export default { export default {
...@@ -13,7 +12,6 @@ export default { ...@@ -13,7 +12,6 @@ export default {
Icon, Icon,
LoadingButton, LoadingButton,
}, },
mixins: [resolvedStatusMixin],
props: { props: {
draft: { draft: {
type: Object, type: Object,
...@@ -43,6 +41,7 @@ export default { ...@@ -43,6 +41,7 @@ export default {
'updateDraft', 'updateDraft',
'publishSingleDraft', 'publishSingleDraft',
'scrollToDraft', 'scrollToDraft',
'toggleResolveDiscussion',
]), ]),
update(data) { update(data) {
this.updateDraft(data); this.updateDraft(data);
...@@ -57,18 +56,10 @@ export default { ...@@ -57,18 +56,10 @@ export default {
this.isEditingDraft = false; this.isEditingDraft = false;
}, },
}, },
showStaysResolved: true,
}; };
</script> </script>
<template> <template>
<article :class="componentClasses" class="draft-note-component note-wrapper"> <article class="draft-note-component note-wrapper">
<header class="draft-note-header">
<strong class="badge draft-pending-label"> {{ __('Pending') }} </strong>
<p v-if="draft.discussion_id" class="draft-note-resolution">
<Icon :size="16" name="status_success" />
{{ __(resolvedStatusMessage) }}
</p>
</header>
<ul class="notes draft-notes"> <ul class="notes draft-notes">
<noteable-note <noteable-note
:note="draft" :note="draft"
...@@ -78,7 +69,12 @@ export default { ...@@ -78,7 +69,12 @@ export default {
@updateSuccess="handleNotEditing" @updateSuccess="handleNotEditing"
@handleDeleteNote="deleteDraft" @handleDeleteNote="deleteDraft"
@handleUpdateNote="update" @handleUpdateNote="update"
/> @toggleResolveStatus="toggleResolveDiscussion(draft.id);"
>
<strong slot="note-header-info" class="badge draft-pending-label append-right-4">
{{ __('Pending') }}
</strong>
</noteable-note>
</ul> </ul>
<template v-if="!isEditingDraft"> <template v-if="!isEditingDraft">
...@@ -88,11 +84,11 @@ export default { ...@@ -88,11 +84,11 @@ export default {
v-html="draftCommands" v-html="draftCommands"
></div> ></div>
<p class="draft-note-actions"> <p class="draft-note-actions d-flex">
<publish-button <publish-button
:show-count="true" :show-count="true"
:should-publish="false" :should-publish="false"
class="btn btn-success btn-inverted" class="btn btn-success btn-inverted append-right-8"
/> />
<loading-button <loading-button
:loading="isPublishingDraft(draft.id) || isPublishing" :loading="isPublishingDraft(draft.id) || isPublishing"
......
import { mapGetters, mapState } from 'vuex'; import { mapGetters, mapState } from 'vuex';
export default { export default {
props: {
isDraft: {
type: Boolean,
required: false,
default: false,
},
},
computed: { computed: {
...mapState({ ...mapState({
withBatchComments: state => state.batchComments && state.batchComments.withBatchComments, withBatchComments: state => state.batchComments && state.batchComments.withBatchComments,
...@@ -21,20 +28,6 @@ export default { ...@@ -21,20 +28,6 @@ export default {
return resolveStatus; return resolveStatus;
}, },
handleUpdate(resolveStatus) {
const beforeSubmitDiscussionState = this.discussionResolved;
this.isSubmitting = true;
const shouldBeResolved = this.shouldBeResolved(resolveStatus) !== beforeSubmitDiscussionState;
this.$emit('handleFormUpdate', this.updatedNoteBody, this.$refs.editNoteForm, () => {
this.isSubmitting = false;
if (resolveStatus || (shouldBeResolved && this.withBatchComments)) {
this.resolveHandler(beforeSubmitDiscussionState); // this will toggle the state
}
});
},
handleAddToReview() { handleAddToReview() {
// check if draft should resolve discussion // check if draft should resolve discussion
const shouldResolve = const shouldResolve =
......
...@@ -2,12 +2,28 @@ import { mapGetters } from 'vuex'; ...@@ -2,12 +2,28 @@ import { mapGetters } from 'vuex';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
export default { export default {
props: {
discussionId: {
type: String,
required: false,
default: null,
},
resolveDiscussion: {
type: Boolean,
required: false,
default: false,
},
},
computed: { computed: {
...mapGetters(['isDiscussionResolved']), ...mapGetters(['isDiscussionResolved']),
resolvedStatusMessage() { resolvedStatusMessage() {
let message; let message;
const discussionResolved = this.isDiscussionResolved(this.draft.discussion_id); const discussionResolved = this.isDiscussionResolved(
const discussionToBeResolved = this.draft.resolve_discussion; this.draft ? this.draft.discussion_id : this.discussionId,
);
const discussionToBeResolved = this.draft
? this.draft.resolve_discussion
: this.resolveDiscussion;
if (discussionToBeResolved && discussionResolved && !this.$options.showStaysResolved) { if (discussionToBeResolved && discussionResolved && !this.$options.showStaysResolved) {
return undefined; return undefined;
...@@ -15,22 +31,20 @@ export default { ...@@ -15,22 +31,20 @@ export default {
if (discussionToBeResolved) { if (discussionToBeResolved) {
if (discussionResolved) { if (discussionResolved) {
message = s__('MergeRequests|Discussion stays resolved.'); message = s__('MergeRequests|Discussion stays resolved');
} else { } else {
message = s__('MergeRequests|Discussion will be resolved.'); message = s__('MergeRequests|Discussion will be resolved');
} }
} else if (discussionResolved) { } else if (discussionResolved) {
message = s__('MergeRequests|Discussion will be unresolved.'); message = s__('MergeRequests|Discussion will be unresolved');
} else if (this.$options.showStaysResolved) { } else if (this.$options.showStaysResolved) {
message = s__('MergeRequests|Discussion stays unresolved.'); message = s__('MergeRequests|Discussion stays unresolved');
} }
return message; return message;
}, },
componentClasses() { componentClasses() {
return this.draft.resolve_discussion return this.resolveDiscussion ? 'is-resolving-discussion' : 'is-unresolving-discussion';
? 'is-resolving-discussion'
: 'is-unresolving-discussion';
}, },
}, },
}; };
...@@ -28,7 +28,11 @@ export default { ...@@ -28,7 +28,11 @@ export default {
discard(endpoint) { discard(endpoint) {
return Vue.http.delete(endpoint); return Vue.http.delete(endpoint);
}, },
update(endpoint, { draftId, note }) { update(endpoint, { draftId, note, resolveDiscussion }) {
return Vue.http.put(`${endpoint}/${draftId}`, { draft_note: { note } }, { emulateJSON: true }); return Vue.http.put(
`${endpoint}/${draftId}`,
{ draft_note: { note, resolve_discussion: resolveDiscussion } },
{ emulateJSON: true },
);
}, },
}; };
...@@ -88,9 +88,13 @@ export const discardReview = ({ commit, getters }) => { ...@@ -88,9 +88,13 @@ export const discardReview = ({ commit, getters }) => {
.catch(() => commit(types.RECEIVE_DISCARD_REVIEW_ERROR)); .catch(() => commit(types.RECEIVE_DISCARD_REVIEW_ERROR));
}; };
export const updateDraft = ({ commit, getters }, { note, noteText, callback }) => export const updateDraft = ({ commit, getters }, { note, noteText, resolveDiscussion, callback }) =>
service service
.update(getters.getNotesData.draftsPath, { draftId: note.id, note: noteText }) .update(getters.getNotesData.draftsPath, {
draftId: note.id,
note: noteText,
resolveDiscussion,
})
.then(res => res.json()) .then(res => res.json())
.then(data => commit(types.RECEIVE_DRAFT_UPDATE_SUCCESS, data)) .then(data => commit(types.RECEIVE_DRAFT_UPDATE_SUCCESS, data))
.then(callback) .then(callback)
...@@ -139,5 +143,9 @@ export const expandAllDiscussions = ({ dispatch, state }) => ...@@ -139,5 +143,9 @@ export const expandAllDiscussions = ({ dispatch, state }) =>
dispatch('expandDiscussion', { discussionId: draft.discussion_id }, { root: true }); dispatch('expandDiscussion', { discussionId: draft.discussion_id }, { root: true });
}); });
export const toggleResolveDiscussion = ({ commit }, draftId) => {
commit(types.TOGGLE_RESOLVE_DISCUSSION, draftId);
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests // prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {}; export default () => {};
...@@ -19,3 +19,5 @@ export const RECEIVE_DRAFT_UPDATE_SUCCESS = 'RECEIVE_DRAFT_UPDATE_SUCCESS'; ...@@ -19,3 +19,5 @@ export const RECEIVE_DRAFT_UPDATE_SUCCESS = 'RECEIVE_DRAFT_UPDATE_SUCCESS';
export const OPEN_REVIEW_DROPDOWN = 'OPEN_REVIEW_DROPDOWN'; export const OPEN_REVIEW_DROPDOWN = 'OPEN_REVIEW_DROPDOWN';
export const CLOSE_REVIEW_DROPDOWN = 'CLOSE_REVIEW_DROPDOWN'; export const CLOSE_REVIEW_DROPDOWN = 'CLOSE_REVIEW_DROPDOWN';
export const TOGGLE_RESOLVE_DISCUSSION = 'TOGGLE_RESOLVE_DISCUSSION';
...@@ -70,4 +70,16 @@ export default { ...@@ -70,4 +70,16 @@ export default {
[types.CLOSE_REVIEW_DROPDOWN](state) { [types.CLOSE_REVIEW_DROPDOWN](state) {
state.showPreviewDropdown = false; state.showPreviewDropdown = false;
}, },
[types.TOGGLE_RESOLVE_DISCUSSION](state, draftId) {
state.drafts = state.drafts.map(draft => {
if (draft.id === draftId) {
return {
...draft,
resolve_discussion: !draft.resolve_discussion,
};
}
return draft;
});
},
}; };
...@@ -47,37 +47,6 @@ button[disabled] { ...@@ -47,37 +47,6 @@ button[disabled] {
} }
} }
.draft-note-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.draft-note-resolution {
padding: 0 $gl-padding;
line-height: 1;
font-size: $label-font-size;
color: $theme-gray-700;
flex-grow: 1;
svg {
vertical-align: text-bottom;
display: inline-block;
}
.is-resolving-discussion & {
svg {
color: $green-600;
}
}
.is-unresolving-discussion & {
svg {
color: $gray-darkest;
}
}
}
.draft-pending-label { .draft-pending-label {
background: $orange-600; background: $orange-600;
color: $white-light; color: $white-light;
...@@ -88,14 +57,10 @@ button[disabled] { ...@@ -88,14 +57,10 @@ button[disabled] {
.diff-file { .diff-file {
.notes .note { .notes .note {
&.draft-note { &.draft-note {
margin: $gl-padding-8 0 $gl-padding; margin: 0 0 $gl-padding;
padding: 0; padding: 0;
border: 0; border: 0;
.note-actions {
margin-top: -26px;
}
&.is-editing { &.is-editing {
margin-bottom: 0; margin-bottom: 0;
} }
......
...@@ -35,58 +35,13 @@ describe('Batch comments draft note component', () => { ...@@ -35,58 +35,13 @@ describe('Batch comments draft note component', () => {
describe('in discussion', () => { describe('in discussion', () => {
beforeEach(done => { beforeEach(done => {
vm.draft.discussion_id = 123; vm.draft.discussion_id = '123';
vm.$nextTick(done); vm.$nextTick(done);
}); });
it('renders resolution status', () => { it('renders resolution status', () => {
expect(vm.$el.querySelector('.draft-note-resolution')).not.toBe(null); expect(vm.$el.querySelector('.line-resolve-btn')).not.toBe(null);
});
describe('resolvedStatusMessage', () => {
describe('resolve discussion', () => {
it('return will be resolved text', () => {
vm.draft.resolve_discussion = true;
expect(vm.resolvedStatusMessage).toBe('Discussion will be resolved.');
});
it('return will be stays resolved text', () => {
spyOnProperty(vm, 'isDiscussionResolved').and.returnValue(() => true);
vm.draft.resolve_discussion = true;
expect(vm.resolvedStatusMessage).toBe('Discussion stays resolved.');
});
});
describe('unresolve discussion', () => {
it('return will be stays unresolved text', () => {
expect(vm.resolvedStatusMessage).toBe('Discussion stays unresolved.');
});
it('return will be unresolved text', () => {
spyOnProperty(vm, 'isDiscussionResolved').and.returnValue(() => true);
vm.$forceUpdate();
expect(vm.resolvedStatusMessage).toBe('Discussion stays unresolved.');
});
});
it('adds resolving class to element', done => {
vm.draft.resolve_discussion = true;
vm.$nextTick(() => {
expect(vm.$el.classList).toContain('is-resolving-discussion');
done();
});
});
it('adds unresolving class to element', () => {
expect(vm.$el.classList).toContain('is-unresolving-discussion');
});
}); });
}); });
...@@ -121,6 +76,7 @@ describe('Batch comments draft note component', () => { ...@@ -121,6 +76,7 @@ describe('Batch comments draft note component', () => {
expect(vm.$store.dispatch).toHaveBeenCalledWith('batchComments/updateDraft', { expect(vm.$store.dispatch).toHaveBeenCalledWith('batchComments/updateDraft', {
note: draft, note: draft,
noteText: 'a', noteText: 'a',
resolveDiscussion: false,
callback: jasmine.any(Function), callback: jasmine.any(Function),
}); });
}) })
......
...@@ -5202,16 +5202,16 @@ msgstr "" ...@@ -5202,16 +5202,16 @@ msgstr ""
msgid "MergeRequests|An error occurred while saving the draft comment." msgid "MergeRequests|An error occurred while saving the draft comment."
msgstr "" msgstr ""
msgid "MergeRequests|Discussion stays resolved." msgid "MergeRequests|Discussion stays resolved"
msgstr "" msgstr ""
msgid "MergeRequests|Discussion stays unresolved." msgid "MergeRequests|Discussion stays unresolved"
msgstr "" msgstr ""
msgid "MergeRequests|Discussion will be resolved." msgid "MergeRequests|Discussion will be resolved"
msgstr "" msgstr ""
msgid "MergeRequests|Discussion will be unresolved." msgid "MergeRequests|Discussion will be unresolved"
msgstr "" msgstr ""
msgid "MergeRequests|Resolve this discussion in a new issue" msgid "MergeRequests|Resolve this discussion in a new issue"
......
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