Commit 782f5fb5 authored by Eulyeon Ko's avatar Eulyeon Ko Committed by Jacques Erasmus

Remove Bootstrap dropdown from comment_form.vue

- Replace Boostrap dropdown with GlDropdown for comment submit button.

- Remove note_dropdown qa selector

- Update the shared example Thread Comments
The shared examples under "Thread Comments" have split into two:
"Thread Comments for commit and snippet"
and "Thread Comments for issue, epic and merge request"

Why?
After we've replaced Bootstrap dropdown button
with GitLab UI counterpart in comment_form.vue,
issue, epic and merge request uses GitLab UI GlDropdown
that has slightly different selectors.

Previously quarantined examples have been restored in this commit:
https://gitlab.com/gitlab-org/gitlab/-/issues/196825
parent d82ba779
<script>
import { GlButton, GlIcon, GlFormCheckbox, GlTooltipDirective } from '@gitlab/ui';
import {
GlButton,
GlIcon,
GlFormCheckbox,
GlTooltipDirective,
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
} from '@gitlab/ui';
import Autosize from 'autosize';
import $ from 'jquery';
import { mapActions, mapGetters, mapState } from 'vuex';
......@@ -25,6 +33,15 @@ import noteSignedOutWidget from './note_signed_out_widget.vue';
export default {
name: 'CommentForm',
i18n: {
submitButton: {
startThread: __('Start thread'),
comment: __('Comment'),
commentHelp: __('Add a general comment to this %{noteableDisplayName}.'),
},
},
noteTypeComment: constants.COMMENT,
noteTypeDiscussion: constants.DISCUSSION,
components: {
noteSignedOutWidget,
discussionLockedWidget,
......@@ -34,6 +51,9 @@ export default {
GlIcon,
CommentFieldLayout,
GlFormCheckbox,
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -63,6 +83,12 @@ export default {
'openState',
]),
...mapState(['isToggleStateButtonLoading']),
isNoteTypeComment() {
return this.noteType === constants.COMMENT;
},
isNoteTypeDiscussion() {
return this.noteType === constants.DISCUSSION;
},
noteableDisplayName() {
return splitCamelCase(this.noteableType).toLowerCase();
},
......@@ -77,6 +103,11 @@ export default {
? __('Discuss a specific suggestion or question that needs to be resolved.')
: __('Discuss a specific suggestion or question.');
},
commentDescription() {
return sprintf(this.$options.i18n.submitButton.commentHelp, {
noteableDisplayName: this.noteableDisplayName,
});
},
isOpen() {
return this.openState === constants.OPENED || this.openState === constants.REOPENED;
},
......@@ -260,6 +291,12 @@ export default {
setNoteType(type) {
this.noteType = type;
},
setNoteTypeToComment() {
this.setNoteType(constants.COMMENT);
},
setNoteTypeToDiscussion() {
this.setNoteType(constants.DISCUSSION);
},
editCurrentUserLastNote() {
if (this.note === '') {
const lastNote = this.getCurrentUserLastNote;
......@@ -354,73 +391,40 @@ export default {
class="gl-text-gray-500"
/>
</gl-form-checkbox>
<div
class="btn-group gl-mr-3 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
<gl-dropdown
split
:text="commentButtonTitle"
class="gl-mr-3 js-comment-button js-comment-submit-button comment-type-dropdown"
category="primary"
variant="success"
:disabled="disableSubmitButton"
data-testid="comment-button"
data-qa-selector="comment_button"
:data-track-label="trackingLabel"
data-track-event="click_button"
@click="handleSave()"
>
<gl-button
:disabled="disableSubmitButton"
class="js-comment-button js-comment-submit-button"
data-qa-selector="comment_button"
data-testid="comment-button"
type="submit"
category="primary"
variant="success"
:data-track-label="trackingLabel"
data-track-event="click_button"
@click.prevent="handleSave()"
>{{ commentButtonTitle }}</gl-button
<gl-dropdown-item
is-check-item
:is-checked="isNoteTypeComment"
:selected="isNoteTypeComment"
@click="setNoteTypeToComment"
>
<gl-button
:disabled="disableSubmitButton"
name="button"
category="primary"
variant="success"
class="note-type-toggle js-note-new-discussion dropdown-toggle"
data-qa-selector="note_dropdown"
data-display="static"
data-toggle="dropdown"
icon="chevron-down"
:aria-label="__('Open comment type dropdown')"
/>
<ul class="note-type-dropdown dropdown-open-top dropdown-menu">
<li :class="{ 'droplab-item-selected': noteType === 'comment' }">
<button
type="button"
class="btn btn-transparent"
@click.prevent="setNoteType('comment')"
>
<gl-icon name="check" class="icon gl-flex-shrink-0" />
<div class="description">
<strong>{{ __('Comment') }}</strong>
<p>
{{
sprintf(__('Add a general comment to this %{noteableDisplayName}.'), {
noteableDisplayName,
})
}}
</p>
</div>
</button>
</li>
<li class="divider droplab-item-ignore"></li>
<li :class="{ 'droplab-item-selected': noteType === 'discussion' }">
<button
type="button"
class="btn btn-transparent"
data-qa-selector="discussion_menu_item"
@click.prevent="setNoteType('discussion')"
>
<gl-icon name="check" class="icon gl-flex-shrink-0" />
<div class="description">
<strong>{{ __('Start thread') }}</strong>
<p>{{ startDiscussionDescription }}</p>
</div>
</button>
</li>
</ul>
</div>
<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>
<gl-button
v-if="hasCloseAndCommentButton && canToggleIssueState"
:loading="isToggleStateButtonLoading"
......
......@@ -394,32 +394,6 @@ table {
}
.comment-type-dropdown {
.btn-success {
width: auto;
}
.dropdown-toggle {
float: right;
i {
color: $white;
padding-right: 2px;
margin-top: 2px;
}
&[disabled] {
i {
color: $gl-text-color-disabled;
}
}
}
.dropdown-menu {
top: initial;
bottom: 100%;
width: 298px;
}
@include media-breakpoint-down(xs) {
display: flex;
width: 100%;
......
---
title: Migrated Bootstrap dropdown to GitLab UI GlDropdown used for comment submit
button
merge_request: 50933
author:
type: other
......@@ -14,5 +14,5 @@ RSpec.describe 'Thread Comments Epic', :js do
visit group_epic_path(epic.group, epic)
end
it_behaves_like 'thread comments', 'epic'
it_behaves_like 'thread comments for issue, epic and merge request', 'epic'
end
......@@ -17,7 +17,6 @@ module QA
element :comment_button
element :comment_field
element :discussion_menu_item
element :note_dropdown
end
base.view 'app/assets/javascripts/notes/components/discussion_actions.vue' do
......@@ -146,7 +145,7 @@ module QA
def start_discussion(text)
fill_element :comment_field, text
click_element :note_dropdown
within_element(:comment_button) { click_button(class: 'dropdown-toggle-split') }
click_element :discussion_menu_item
click_element :comment_button
......
......@@ -18,7 +18,7 @@ RSpec.describe 'Thread Comments Commit', :js do
visit project_commit_path(project, sample_commit.id)
end
it_behaves_like 'thread comments', 'commit'
it_behaves_like 'thread comments for commit and snippet', 'commit'
it 'has class .js-note-emoji' do
expect(page).to have_css('.js-note-emoji')
......
......@@ -16,5 +16,5 @@ RSpec.describe 'Thread Comments Issue', :js do
visit project_issue_path(project, issue)
end
it_behaves_like 'thread comments', 'issue'
it_behaves_like 'thread comments for issue, epic and merge request', 'issue'
end
......@@ -20,5 +20,5 @@ RSpec.describe 'Thread Comments Merge Request', :js do
wait_for_requests
end
it_behaves_like 'thread comments', 'merge request'
it_behaves_like 'thread comments for issue, epic and merge request', 'merge request'
end
......@@ -22,7 +22,7 @@ RSpec.describe 'Thread Comments Snippet', :js do
visit project_snippet_path(project, snippet)
end
it_behaves_like 'thread comments', 'snippet'
it_behaves_like 'thread comments for commit and snippet', 'snippet'
end
context 'with personal snippets' do
......@@ -32,6 +32,6 @@ RSpec.describe 'Thread Comments Snippet', :js do
visit snippet_path(snippet)
end
it_behaves_like 'thread comments', 'snippet'
it_behaves_like 'thread comments for commit and snippet', 'snippet'
end
end
......@@ -44,7 +44,10 @@ RSpec.describe 'Merge request > User posts notes', :js do
it 'has enable submit button, preview button and saves content to local storage' do
page.within('.js-main-target-form') do
expect(page).not_to have_css('.js-comment-button[disabled]')
page.within('[data-testid="comment-button"]') do
expect(page).to have_css('.split-content-button')
expect(page).not_to have_css('.split-content-button[disabled]')
end
expect(page).to have_css('.js-md-preview-button', visible: true)
end
......
import { GlDropdown } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import Autosize from 'autosize';
import MockAdapter from 'axios-mock-adapter';
......@@ -23,9 +24,10 @@ describe('issue_comment_form component', () => {
let axiosMock;
const findCloseReopenButton = () => wrapper.findByTestId('close-reopen-button');
const findCommentButton = () => wrapper.findByTestId('comment-button');
const findTextArea = () => wrapper.findByTestId('comment-field');
const findConfidentialNoteCheckbox = () => wrapper.findByTestId('confidential-note-checkbox');
const findCommentGlDropdown = () => wrapper.find(GlDropdown);
const findCommentButton = () => findCommentGlDropdown().find('button');
const createNotableDataMock = (data = {}) => {
return {
......@@ -243,7 +245,7 @@ describe('issue_comment_form component', () => {
it('should render comment button as disabled', () => {
mountComponent();
expect(findCommentButton().props('disabled')).toBe(true);
expect(findCommentGlDropdown().props('disabled')).toBe(true);
});
it('should enable comment button if it has note', async () => {
......@@ -251,7 +253,7 @@ describe('issue_comment_form component', () => {
await wrapper.setData({ note: 'Foo' });
expect(findCommentButton().props('disabled')).toBe(false);
expect(findCommentGlDropdown().props('disabled')).toBe(false);
});
it('should update buttons texts when it has note', () => {
......@@ -437,7 +439,7 @@ describe('issue_comment_form component', () => {
await wrapper.vm.$nextTick();
// submit comment
wrapper.findByTestId('comment-button').trigger('click');
findCommentButton().trigger('click');
const [providedData] = wrapper.vm.saveNote.mock.calls[0];
expect(providedData.data.note.confidential).toBe(shouldCheckboxBeChecked);
......
......@@ -33,6 +33,8 @@ describe('note_app', () => {
let wrapper;
let store;
const findCommentButton = () => wrapper.find('[data-testid="comment-button"]');
const getComponentOrder = () => {
return wrapper
.findAll('#notes-list,.js-comment-form')
......@@ -144,7 +146,7 @@ describe('note_app', () => {
});
it('should render form comment button as disabled', () => {
expect(wrapper.find('.js-note-new-discussion').attributes('disabled')).toEqual('disabled');
expect(findCommentButton().props('disabled')).toEqual(true);
});
it('updates discussions badge', () => {
......
......@@ -93,6 +93,6 @@ end
def submit_time(quick_action)
fill_in 'note[note]', with: quick_action
find('.js-comment-submit-button').click
find('[data-testid="comment-button"]').click
wait_for_requests
end
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