Commit 0c88cb3b authored by Mike Greiling's avatar Mike Greiling

Merge branch 'mg-separate-comment-type-dropdown-logic' into 'master'

Split "comment type" dropdown into a new component

See merge request gitlab-org/gitlab!68505
parents 687e5dff 496dcda4
<script>
import {
GlAlert,
GlButton,
GlIcon,
GlFormCheckbox,
GlTooltipDirective,
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
} from '@gitlab/ui';
import { GlAlert, GlButton, GlIcon, GlFormCheckbox, GlTooltipDirective } from '@gitlab/ui';
import Autosize from 'autosize';
import $ from 'jquery';
import { mapActions, mapGetters, mapState } from 'vuex';
......@@ -34,6 +25,7 @@ import { COMMENT_FORM } from '../i18n';
import issuableStateMixin from '../mixins/issuable_state';
import CommentFieldLayout from './comment_field_layout.vue';
import CommentTypeDropdown from './comment_type_dropdown.vue';
import discussionLockedWidget from './discussion_locked_widget.vue';
import noteSignedOutWidget from './note_signed_out_widget.vue';
......@@ -42,8 +34,6 @@ const { UNPROCESSABLE_ENTITY } = httpStatusCodes;
export default {
name: 'CommentForm',
i18n: COMMENT_FORM,
noteTypeComment: constants.COMMENT,
noteTypeDiscussion: constants.DISCUSSION,
components: {
noteSignedOutWidget,
discussionLockedWidget,
......@@ -53,10 +43,8 @@ export default {
TimelineEntryItem,
GlIcon,
CommentFieldLayout,
CommentTypeDropdown,
GlFormCheckbox,
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -88,12 +76,6 @@ export default {
'hasDrafts',
]),
...mapState(['isToggleStateButtonLoading']),
isNoteTypeComment() {
return this.noteType === constants.COMMENT;
},
isNoteTypeDiscussion() {
return this.noteType === constants.DISCUSSION;
},
noteableDisplayName() {
return splitCamelCase(this.noteableType).toLowerCase();
},
......@@ -105,15 +87,8 @@ export default {
? this.$options.i18n.comment
: this.$options.i18n.startThread;
},
startDiscussionDescription() {
return this.getNoteableData.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE
? this.$options.i18n.discussionThatNeedsResolution
: this.$options.i18n.discussion;
},
commentDescription() {
return sprintf(this.$options.i18n.submitButton.commentHelp, {
noteableDisplayName: this.noteableDisplayName,
});
discussionsRequireResolution() {
return this.getNoteableData.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE;
},
isOpen() {
return this.openState === constants.OPENED || this.openState === constants.REOPENED;
......@@ -314,15 +289,6 @@ export default {
this.autosave.reset();
},
setNoteType(type) {
this.noteType = type;
},
setNoteTypeToComment() {
this.setNoteType(constants.COMMENT);
},
setNoteTypeToDiscussion() {
this.setNoteType(constants.DISCUSSION);
},
editCurrentUserLastNote() {
if (this.note === '') {
const lastNote = this.getCurrentUserLastNote;
......@@ -448,40 +414,15 @@ export default {
class="gl-text-gray-500"
/>
</gl-form-checkbox>
<gl-dropdown
split
:text="commentButtonTitle"
class="gl-mr-3 js-comment-button js-comment-submit-button comment-type-dropdown"
category="primary"
variant="confirm"
<comment-type-dropdown
v-model="noteType"
class="gl-mr-3"
:disabled="disableSubmitButton"
data-testid="comment-button"
data-qa-selector="comment_button"
:data-track-label="trackingLabel"
data-track-event="click_button"
@click="handleSave()"
>
<gl-dropdown-item
is-check-item
:is-checked="isNoteTypeComment"
:selected="isNoteTypeComment"
@click="setNoteTypeToComment"
>
<strong>{{ $options.i18n.submitButton.comment }}</strong>
<p class="gl-m-0">{{ commentDescription }}</p>
</gl-dropdown-item>
<gl-dropdown-divider />
<gl-dropdown-item
is-check-item
:is-checked="isNoteTypeDiscussion"
:selected="isNoteTypeDiscussion"
data-qa-selector="discussion_menu_item"
@click="setNoteTypeToDiscussion"
>
<strong>{{ $options.i18n.submitButton.startThread }}</strong>
<p class="gl-m-0">{{ startDiscussionDescription }}</p>
</gl-dropdown-item>
</gl-dropdown>
:tracking-label="trackingLabel"
:noteable-display-name="noteableDisplayName"
:discussions-require-resolution="discussionsRequireResolution"
@click="handleSave"
/>
</template>
<gl-button
v-if="canToggleIssueState"
......
<script>
import { GlDropdown, GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
import { sprintf } from '~/locale';
import { COMMENT_FORM } from '~/notes/i18n';
import * as constants from '../constants';
export default {
i18n: COMMENT_FORM,
components: {
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
},
model: {
prop: 'noteType',
event: 'change',
},
props: {
disabled: {
type: Boolean,
required: false,
default: false,
},
trackingLabel: {
type: String,
required: false,
default: undefined,
},
discussionsRequireResolution: {
type: Boolean,
required: false,
default: false,
},
noteableDisplayName: {
type: String,
required: true,
},
noteType: {
type: String,
required: true,
},
},
computed: {
isNoteTypeComment() {
return this.noteType === constants.COMMENT;
},
isNoteTypeDiscussion() {
return this.noteType === constants.DISCUSSION;
},
commentButtonTitle() {
return this.noteType === constants.COMMENT
? this.$options.i18n.comment
: this.$options.i18n.startThread;
},
startDiscussionDescription() {
return this.discussionsRequireResolution
? this.$options.i18n.discussionThatNeedsResolution
: this.$options.i18n.discussion;
},
commentDescription() {
return sprintf(this.$options.i18n.submitButton.commentHelp, {
noteableDisplayName: this.noteableDisplayName,
});
},
},
methods: {
handleClick() {
this.$emit('click');
},
setNoteTypeToComment() {
if (this.noteType !== constants.COMMENT) {
this.$emit('change', constants.COMMENT);
}
},
setNoteTypeToDiscussion() {
if (this.noteType !== constants.DISCUSSION) {
this.$emit('change', constants.DISCUSSION);
}
},
},
};
</script>
<template>
<gl-dropdown
split
:text="commentButtonTitle"
class="gl-mr-3 js-comment-button js-comment-submit-button comment-type-dropdown"
category="primary"
variant="confirm"
:disabled="disabled"
data-testid="comment-button"
data-qa-selector="comment_button"
:data-track-label="trackingLabel"
data-track-event="click_button"
@click="$emit('click')"
>
<gl-dropdown-item is-check-item :is-checked="isNoteTypeComment" @click="setNoteTypeToComment">
<strong>{{ $options.i18n.submitButton.comment }}</strong>
<p class="gl-m-0">{{ commentDescription }}</p>
</gl-dropdown-item>
<gl-dropdown-divider />
<gl-dropdown-item
is-check-item
:is-checked="isNoteTypeDiscussion"
data-qa-selector="discussion_menu_item"
@click="setNoteTypeToDiscussion"
>
<strong>{{ $options.i18n.submitButton.startThread }}</strong>
<p class="gl-m-0">{{ startDiscussionDescription }}</p>
</gl-dropdown-item>
</gl-dropdown>
</template>
......@@ -14,8 +14,11 @@ module QA
end
base.view 'app/assets/javascripts/notes/components/comment_form.vue' do
element :comment_button
element :comment_field
end
base.view 'app/assets/javascripts/notes/components/comment_type_dropdown.vue' do
element :comment_button
element :discussion_menu_item
end
......
......@@ -10,6 +10,7 @@ import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import CommentForm from '~/notes/components/comment_form.vue';
import CommentTypeDropdown from '~/notes/components/comment_type_dropdown.vue';
import * as constants from '~/notes/constants';
import eventHub from '~/notes/event_hub';
import { COMMENT_FORM } from '~/notes/i18n';
......@@ -33,8 +34,8 @@ describe('issue_comment_form component', () => {
const findAddToReviewButton = () => wrapper.findByTestId('add-to-review-button');
const findAddCommentNowButton = () => wrapper.findByTestId('add-comment-now-button');
const findConfidentialNoteCheckbox = () => wrapper.findByTestId('confidential-note-checkbox');
const findCommentGlDropdown = () => wrapper.findByTestId('comment-button');
const findCommentButton = () => findCommentGlDropdown().find('button');
const findCommentTypeDropdown = () => wrapper.findComponent(CommentTypeDropdown);
const findCommentButton = () => findCommentTypeDropdown().find('button');
const findErrorAlerts = () => wrapper.findAllComponents(GlAlert).wrappers;
async function clickCommentButton({ waitForComponent = true, waitForNetwork = true } = {}) {
......@@ -381,7 +382,7 @@ describe('issue_comment_form component', () => {
it('should render comment button as disabled', () => {
mountComponent();
expect(findCommentGlDropdown().props('disabled')).toBe(true);
expect(findCommentTypeDropdown().props('disabled')).toBe(true);
});
it('should enable comment button if it has note', async () => {
......@@ -389,7 +390,7 @@ describe('issue_comment_form component', () => {
await wrapper.setData({ note: 'Foo' });
expect(findCommentGlDropdown().props('disabled')).toBe(false);
expect(findCommentTypeDropdown().props('disabled')).toBe(false);
});
it('should update buttons texts when it has note', () => {
......@@ -624,7 +625,7 @@ describe('issue_comment_form component', () => {
it('when no drafts exist, should not render', () => {
mountComponent();
expect(findCommentGlDropdown().exists()).toBe(true);
expect(findCommentTypeDropdown().exists()).toBe(true);
expect(findAddToReviewButton().exists()).toBe(false);
expect(findAddCommentNowButton().exists()).toBe(false);
});
......@@ -637,7 +638,7 @@ describe('issue_comment_form component', () => {
it('should render', () => {
mountComponent();
expect(findCommentGlDropdown().exists()).toBe(false);
expect(findCommentTypeDropdown().exists()).toBe(false);
expect(findAddToReviewButton().exists()).toBe(true);
expect(findAddCommentNowButton().exists()).toBe(true);
});
......
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import CommentTypeDropdown from '~/notes/components/comment_type_dropdown.vue';
import * as constants from '~/notes/constants';
import { COMMENT_FORM } from '~/notes/i18n';
describe('CommentTypeDropdown component', () => {
let wrapper;
const findCommentGlDropdown = () => wrapper.findComponent(GlDropdown);
const findCommentDropdownOption = () => wrapper.findAllComponents(GlDropdownItem).at(0);
const findDiscussionDropdownOption = () => wrapper.findAllComponents(GlDropdownItem).at(1);
const mountComponent = ({ props = {} } = {}) => {
wrapper = extendedWrapper(
mount(CommentTypeDropdown, {
propsData: {
noteableDisplayName: 'issue',
noteType: constants.COMMENT,
...props,
},
}),
);
};
afterEach(() => {
wrapper.destroy();
});
it('Should label action button "Comment" and correct dropdown item checked when selected', () => {
mountComponent({ props: { noteType: constants.COMMENT } });
expect(findCommentGlDropdown().props()).toMatchObject({ text: COMMENT_FORM.comment });
expect(findCommentDropdownOption().props()).toMatchObject({ isChecked: true });
expect(findDiscussionDropdownOption().props()).toMatchObject({ isChecked: false });
});
it('Should label action button "Start Thread" and correct dropdown item option checked when selected', () => {
mountComponent({ props: { noteType: constants.DISCUSSION } });
expect(findCommentGlDropdown().props()).toMatchObject({ text: COMMENT_FORM.startThread });
expect(findCommentDropdownOption().props()).toMatchObject({ isChecked: false });
expect(findDiscussionDropdownOption().props()).toMatchObject({ isChecked: true });
});
it('Should emit `change` event when clicking on an alternate dropdown option', () => {
mountComponent({ props: { noteType: constants.DISCUSSION } });
findCommentDropdownOption().vm.$emit('click');
findDiscussionDropdownOption().vm.$emit('click');
expect(wrapper.emitted('change')[0]).toEqual([constants.COMMENT]);
expect(wrapper.emitted('change').length).toEqual(1);
});
it('Should emit `click` event when clicking on the action button', () => {
mountComponent({ props: { noteType: constants.DISCUSSION } });
findCommentGlDropdown().vm.$emit('click');
expect(wrapper.emitted('click').length > 0).toBe(true);
});
});
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