Commit 5b012457 authored by Jacques Erasmus's avatar Jacques Erasmus

Merge branch 'remove-bootstrap-dropdowns-from-note-components' into 'master'

Migrate Bootstrap dropdown to GitLab UI GlDropdown in comment_form.vue

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