Commit 476c0030 authored by Axel García's avatar Axel García

Adds a toggle for adding private comments

This allows to create a private comment that is accessible by internal
staff only
parent cd5f3d3e
......@@ -3,7 +3,15 @@ import $ from 'jquery';
import { mapActions, mapGetters, mapState } from 'vuex';
import { isEmpty } from 'lodash';
import Autosize from 'autosize';
import { GlAlert, GlIntersperse, GlLink, GlSprintf } from '@gitlab/ui';
import {
GlAlert,
GlFormCheckbox,
GlIcon,
GlIntersperse,
GlLink,
GlSprintf,
GlTooltipDirective,
} from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import Flash from '../../flash';
......@@ -24,6 +32,7 @@ import loadingButton from '../../vue_shared/components/loading_button.vue';
import noteSignedOutWidget from './note_signed_out_widget.vue';
import discussionLockedWidget from './discussion_locked_widget.vue';
import issuableStateMixin from '../mixins/issuable_state';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'CommentForm',
......@@ -36,11 +45,16 @@ export default {
loadingButton,
TimelineEntryItem,
GlAlert,
GlFormCheckbox,
GlIcon,
GlIntersperse,
GlLink,
GlSprintf,
},
mixins: [issuableStateMixin],
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [issuableStateMixin, glFeatureFlagsMixin()],
props: {
noteableType: {
type: String,
......@@ -51,6 +65,7 @@ export default {
return {
note: '',
noteType: constants.COMMENT,
noteIsConfidential: false,
isSubmitting: false,
isSubmitButtonDisabled: true,
};
......@@ -138,6 +153,9 @@ export default {
trackingLabel() {
return slugifyWithUnderscore(`${this.commentButtonTitle} button`);
},
confidentialNotesEnabled() {
return Boolean(this.glFeatures.confidentialNotes);
},
},
watch: {
note(newNote) {
......@@ -185,6 +203,7 @@ export default {
note: {
noteable_type: this.noteableType,
noteable_id: this.getNoteableData.id,
confidential: this.noteIsConfidential,
note: this.note,
},
merge_request_diff_head_sha: this.getNoteableData.diff_head_sha,
......@@ -285,6 +304,7 @@ export default {
if (shouldClear) {
this.note = '';
this.noteIsConfidential = false;
this.resizeTextarea();
this.$refs.markdownField.previewMarkdown = false;
}
......@@ -411,6 +431,19 @@ js-gfm-input js-autosize markdown-area js-vue-textarea qa-comment-input"
</p>
</gl-alert>
<div class="note-form-actions">
<div v-if="confidentialNotesEnabled" class="js-confidential-note-toggle mb-4">
<gl-form-checkbox v-model="noteIsConfidential">
<gl-icon name="eye-slash" :size="12" />
{{ __('Mark this comment as private') }}
<gl-icon
v-gl-tooltip:tooltipcontainer.bottom
name="question"
:size="12"
:title="__('Private comments are accessible by internal staff only')"
class="gl-text-gray-800"
/>
</gl-form-checkbox>
</div>
<div
class="float-left btn-group
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
......
......@@ -52,6 +52,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action only: :show do
push_frontend_feature_flag(:real_time_issue_sidebar, @project)
push_frontend_feature_flag(:confidential_notes, @project)
end
around_action :allow_gitaly_ref_name_caching, only: [:discussions]
......
......@@ -12718,6 +12718,9 @@ msgstr ""
msgid "Mark comment as resolved"
msgstr ""
msgid "Mark this comment as private"
msgstr ""
msgid "Mark this issue as a duplicate of another issue"
msgstr ""
......@@ -15489,6 +15492,9 @@ msgstr ""
msgid "Private - The group and its projects can only be viewed by members."
msgstr ""
msgid "Private comments are accessible by internal staff only"
msgstr ""
msgid "Private group(s)"
msgstr ""
......
......@@ -24,6 +24,7 @@ describe('issue_comment_form component', () => {
let store;
let wrapper;
let axiosMock;
let features = {};
const setupStore = (userData, noteableData) => {
store.dispatch('setUserData', userData);
......@@ -37,12 +38,16 @@ describe('issue_comment_form component', () => {
noteableType,
},
store,
provide: {
glFeatures: features,
},
});
};
beforeEach(() => {
axiosMock = new MockAdapter(axios);
store = createStore();
features = {};
});
afterEach(() => {
......@@ -298,6 +303,32 @@ describe('issue_comment_form component', () => {
});
});
});
describe('when note can be confidential', () => {
it('appends confidential status to note payload when saving', () => {
jest.spyOn(wrapper.vm, 'saveNote').mockReturnValue(new Promise(() => {}));
wrapper.vm.note = 'confidential note';
return wrapper.vm.$nextTick().then(() => {
wrapper.find('.js-comment-submit-button').trigger('click');
const [providedData] = wrapper.vm.saveNote.mock.calls[0];
expect(providedData.data.note.confidential).toBe(false);
});
});
it('should render confidential toggle as false', () => {
features = { confidentialNotes: true };
mountComponent();
const input = wrapper.find('.js-confidential-note-toggle .form-check-input');
expect(input.exists()).toBe(true);
expect(input.attributes('checked')).toBeFalsy();
});
});
});
describe('issue is confidential', () => {
......
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