Commit 84e1fbdd authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch...

Merge branch '229325-migrate-bootstrap-button-to-gitlab-ui-glbutton-in-app-assets-javascripts-notes-components' into 'master'

Migrate MR threads toolbar to GitLab UI

See merge request gitlab-org/gitlab!41658
parents 8af21fd3 f1f16e0e
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; import { GlTooltipDirective, GlIcon, GlButton, GlButtonGroup } from '@gitlab/ui';
import { __ } from '~/locale';
import discussionNavigation from '../mixins/discussion_navigation'; import discussionNavigation from '../mixins/discussion_navigation';
export default { export default {
...@@ -9,6 +10,8 @@ export default { ...@@ -9,6 +10,8 @@ export default {
}, },
components: { components: {
GlIcon, GlIcon,
GlButton,
GlButtonGroup,
}, },
mixins: [discussionNavigation], mixins: [discussionNavigation],
computed: { computed: {
...@@ -34,6 +37,12 @@ export default { ...@@ -34,6 +37,12 @@ export default {
allExpanded() { allExpanded() {
return this.toggeableDiscussions.every(discussion => discussion.expanded); return this.toggeableDiscussions.every(discussion => discussion.expanded);
}, },
lineResolveClass() {
return this.allResolved ? 'line-resolve-btn is-active' : 'line-resolve-text';
},
toggleThreadsLabel() {
return this.allExpanded ? __('Collapse all threads') : __('Expand all threads');
},
}, },
methods: { methods: {
...mapActions(['setExpandDiscussions']), ...mapActions(['setExpandDiscussions']),
...@@ -51,59 +60,49 @@ export default { ...@@ -51,59 +60,49 @@ export default {
<div <div
v-if="resolvableDiscussionsCount > 0" v-if="resolvableDiscussionsCount > 0"
ref="discussionCounter" ref="discussionCounter"
class="line-resolve-all-container full-width-mobile" class="line-resolve-all-container full-width-mobile gl-display-flex d-sm-flex"
> >
<div class="full-width-mobile d-flex d-sm-flex"> <div class="line-resolve-all">
<div class="line-resolve-all"> <span :class="lineResolveClass">
<span <template v-if="allResolved">
:class="{ 'line-resolve-btn is-active': allResolved, 'line-resolve-text': !allResolved }" <gl-icon name="check-circle-filled" />
> {{ __('All threads resolved') }}
<template v-if="allResolved"> </template>
<gl-icon name="check-circle-filled" /> <template v-else>
{{ __('All threads resolved') }} {{ n__('%d unresolved thread', '%d unresolved threads', unresolvedDiscussionsCount) }}
</template> </template>
<template v-else> </span>
{{ n__('%d unresolved thread', '%d unresolved threads', unresolvedDiscussionsCount) }}
</template>
</span>
</div>
<div
v-if="resolveAllDiscussionsIssuePath && !allResolved"
class="btn-group btn-group-sm"
role="group"
>
<a
v-gl-tooltip
:href="resolveAllDiscussionsIssuePath"
:title="s__('Resolve all threads in new issue')"
class="new-issue-for-discussion btn btn-default discussion-create-issue-btn"
>
<gl-icon name="issue-new" />
</a>
</div>
<div v-if="isLoggedIn && !allResolved" class="btn-group btn-group-sm" role="group">
<button
v-gl-tooltip
:title="__('Jump to next unresolved thread')"
class="btn btn-default discussion-next-btn"
data-track-event="click_button"
data-track-label="mr_next_unresolved_thread"
data-track-property="click_next_unresolved_thread_top"
@click="jumpToNextDiscussion"
>
<gl-icon name="comment-next" />
</button>
</div>
<div class="btn-group btn-group-sm" role="group">
<button
v-gl-tooltip
:title="__('Toggle all threads')"
class="btn btn-default toggle-all-discussions-btn"
@click="handleExpandDiscussions"
>
<gl-icon :name="allExpanded ? 'angle-up' : 'angle-down'" />
</button>
</div>
</div> </div>
<gl-button-group>
<gl-button
v-if="resolveAllDiscussionsIssuePath && !allResolved"
v-gl-tooltip
:href="resolveAllDiscussionsIssuePath"
:title="s__('Resolve all threads in new issue')"
:aria-label="s__('Resolve all threads in new issue')"
class="new-issue-for-discussion discussion-create-issue-btn"
icon="issue-new"
/>
<gl-button
v-if="isLoggedIn && !allResolved"
v-gl-tooltip
:title="__('Jump to next unresolved thread')"
:aria-label="__('Jump to next unresolved thread')"
class="discussion-next-btn"
data-track-event="click_button"
data-track-label="mr_next_unresolved_thread"
data-track-property="click_next_unresolved_thread_top"
icon="comment-next"
@click="jumpToNextDiscussion"
/>
<gl-button
v-gl-tooltip
:title="toggleThreadsLabel"
:aria-label="toggleThreadsLabel"
class="toggle-all-discussions-btn"
:icon="allExpanded ? 'angle-up' : 'angle-down'"
@click="handleExpandDiscussions"
/>
</gl-button-group>
</div> </div>
</template> </template>
...@@ -858,68 +858,28 @@ $note-form-margin-left: 72px; ...@@ -858,68 +858,28 @@ $note-form-margin-left: 72px;
} }
.line-resolve-all-container { .line-resolve-all-container {
margin: $gl-padding-4;
> div { > div {
white-space: nowrap; white-space: nowrap;
} }
.discussion-next-btn { .btn-group .btn:first-child {
border-radius: 0;
}
.toggle-all-discussions-btn {
border-top-left-radius: 0; border-top-left-radius: 0;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
.btn {
line-height: $gl-line-height;
svg {
fill: $gray-500;
}
&.discussion-create-issue-btn {
border-radius: 0;
border-right: 0;
a {
padding: 0;
line-height: 0;
&:hover {
text-decoration: none;
border: 0;
}
}
}
&.discussion-next-btn {
border-right: 0;
}
}
} }
.line-resolve-all { .line-resolve-all {
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
padding: $gl-padding-4 10px; padding: $gl-padding-8 $gl-padding-12;
background-color: $gray-light; background-color: $gray-light;
border: 1px solid $border-color; border: 1px solid $border-color;
border-right: 0;
border-radius: $border-radius-default; border-radius: $border-radius-default;
font-size: $gl-btn-small-font-size;
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
border-right: 0; font-size: $gl-font-size;
line-height: 1rem;
.line-resolve-btn {
color: $gray-500;
svg {
vertical-align: text-top;
}
}
@include media-breakpoint-down(xs) { @include media-breakpoint-down(xs) {
flex: 1; flex: 1;
......
...@@ -6382,6 +6382,9 @@ msgstr "" ...@@ -6382,6 +6382,9 @@ msgstr ""
msgid "Collapse" msgid "Collapse"
msgstr "" msgstr ""
msgid "Collapse all threads"
msgstr ""
msgid "Collapse approvers" msgid "Collapse approvers"
msgstr "" msgstr ""
...@@ -10458,6 +10461,9 @@ msgstr "" ...@@ -10458,6 +10461,9 @@ msgstr ""
msgid "Expand all files" msgid "Expand all files"
msgstr "" msgstr ""
msgid "Expand all threads"
msgstr ""
msgid "Expand approvers" msgid "Expand approvers"
msgstr "" msgstr ""
...@@ -26807,9 +26813,6 @@ msgstr "" ...@@ -26807,9 +26813,6 @@ msgstr ""
msgid "Toggle Sidebar" msgid "Toggle Sidebar"
msgstr "" msgstr ""
msgid "Toggle all threads"
msgstr ""
msgid "Toggle backtrace" msgid "Toggle backtrace"
msgstr "" msgstr ""
......
import Vuex from 'vuex'; import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlIcon } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import notesModule from '~/notes/stores/modules'; import notesModule from '~/notes/stores/modules';
import DiscussionCounter from '~/notes/components/discussion_counter.vue'; import DiscussionCounter from '~/notes/components/discussion_counter.vue';
import { noteableDataMock, discussionMock, notesDataMock, userDataMock } from '../mock_data'; import { noteableDataMock, discussionMock, notesDataMock, userDataMock } from '../mock_data';
...@@ -9,6 +9,7 @@ import * as types from '~/notes/stores/mutation_types'; ...@@ -9,6 +9,7 @@ import * as types from '~/notes/stores/mutation_types';
describe('DiscussionCounter component', () => { describe('DiscussionCounter component', () => {
let store; let store;
let wrapper; let wrapper;
let setExpandDiscussionsFn;
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -16,6 +17,7 @@ describe('DiscussionCounter component', () => { ...@@ -16,6 +17,7 @@ describe('DiscussionCounter component', () => {
beforeEach(() => { beforeEach(() => {
window.mrTabs = {}; window.mrTabs = {};
const { state, getters, mutations, actions } = notesModule(); const { state, getters, mutations, actions } = notesModule();
setExpandDiscussionsFn = jest.fn().mockImplementation(actions.setExpandDiscussions);
store = new Vuex.Store({ store = new Vuex.Store({
state: { state: {
...@@ -24,7 +26,10 @@ describe('DiscussionCounter component', () => { ...@@ -24,7 +26,10 @@ describe('DiscussionCounter component', () => {
}, },
getters, getters,
mutations, mutations,
actions, actions: {
...actions,
setExpandDiscussions: setExpandDiscussionsFn,
},
}); });
store.dispatch('setNoteableData', { store.dispatch('setNoteableData', {
...noteableDataMock, ...noteableDataMock,
...@@ -84,7 +89,7 @@ describe('DiscussionCounter component', () => { ...@@ -84,7 +89,7 @@ describe('DiscussionCounter component', () => {
wrapper = shallowMount(DiscussionCounter, { store, localVue }); wrapper = shallowMount(DiscussionCounter, { store, localVue });
expect(wrapper.find(`.is-active`).exists()).toBe(isActive); expect(wrapper.find(`.is-active`).exists()).toBe(isActive);
expect(wrapper.findAll('[role="group"').length).toBe(groupLength); expect(wrapper.findAll(GlButton)).toHaveLength(groupLength);
}); });
}); });
...@@ -103,23 +108,22 @@ describe('DiscussionCounter component', () => { ...@@ -103,23 +108,22 @@ describe('DiscussionCounter component', () => {
it('calls button handler when clicked', () => { it('calls button handler when clicked', () => {
updateStoreWithExpanded(true); updateStoreWithExpanded(true);
wrapper.setMethods({ handleExpandDiscussions: jest.fn() }); toggleAllButton.vm.$emit('click');
toggleAllButton.trigger('click');
expect(wrapper.vm.handleExpandDiscussions).toHaveBeenCalledTimes(1); expect(setExpandDiscussionsFn).toHaveBeenCalledTimes(1);
}); });
it('collapses all discussions if expanded', () => { it('collapses all discussions if expanded', () => {
updateStoreWithExpanded(true); updateStoreWithExpanded(true);
expect(wrapper.vm.allExpanded).toBe(true); expect(wrapper.vm.allExpanded).toBe(true);
expect(toggleAllButton.find(GlIcon).props().name).toBe('angle-up'); expect(toggleAllButton.props('icon')).toBe('angle-up');
toggleAllButton.trigger('click'); toggleAllButton.vm.$emit('click');
return wrapper.vm.$nextTick().then(() => { return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.allExpanded).toBe(false); expect(wrapper.vm.allExpanded).toBe(false);
expect(toggleAllButton.find(GlIcon).props().name).toBe('angle-down'); expect(toggleAllButton.props('icon')).toBe('angle-down');
}); });
}); });
...@@ -127,13 +131,13 @@ describe('DiscussionCounter component', () => { ...@@ -127,13 +131,13 @@ describe('DiscussionCounter component', () => {
updateStoreWithExpanded(false); updateStoreWithExpanded(false);
expect(wrapper.vm.allExpanded).toBe(false); expect(wrapper.vm.allExpanded).toBe(false);
expect(toggleAllButton.find(GlIcon).props().name).toBe('angle-down'); expect(toggleAllButton.props('icon')).toBe('angle-down');
toggleAllButton.trigger('click'); toggleAllButton.vm.$emit('click');
return wrapper.vm.$nextTick().then(() => { return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.allExpanded).toBe(true); expect(wrapper.vm.allExpanded).toBe(true);
expect(toggleAllButton.find(GlIcon).props().name).toBe('angle-up'); expect(toggleAllButton.props('icon')).toBe('angle-up');
}); });
}); });
}); });
......
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