Commit c7a6ab39 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'labels-widget-polish' into 'master'

Labels widget architecture polish

See merge request gitlab-org/gitlab!76707
parents 96ea81ae 4e41c7e6
<script> <script>
import { GlLabel } from '@gitlab/ui'; import { GlIcon, GlLabel, GlTooltipDirective } from '@gitlab/ui';
import { sortBy } from 'lodash'; import { sortBy } from 'lodash';
import { isScopedLabel } from '~/lib/utils/common_utils'; import { isScopedLabel } from '~/lib/utils/common_utils';
import { s__, sprintf } from '~/locale';
export default { export default {
directives: {
GlTooltip: GlTooltipDirective,
},
components: { components: {
GlIcon,
GlLabel, GlLabel,
}, },
inject: ['allowScopedLabels'], inject: ['allowScopedLabels'],
...@@ -35,6 +40,23 @@ export default { ...@@ -35,6 +40,23 @@ export default {
sortedSelectedLabels() { sortedSelectedLabels() {
return sortBy(this.selectedLabels, (label) => (isScopedLabel(label) ? 0 : 1)); return sortBy(this.selectedLabels, (label) => (isScopedLabel(label) ? 0 : 1));
}, },
labelsList() {
const labelsString = this.selectedLabels.length
? this.selectedLabels
.slice(0, 5)
.map((label) => label.title)
.join(', ')
: s__('LabelSelect|Labels');
if (this.selectedLabels.length > 5) {
return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), {
labelsString,
remainingLabelCount: this.selectedLabels.length - 5,
});
}
return labelsString;
},
}, },
methods: { methods: {
labelFilterUrl(label) { labelFilterUrl(label) {
...@@ -48,6 +70,9 @@ export default { ...@@ -48,6 +70,9 @@ export default {
removeLabel(labelId) { removeLabel(labelId) {
this.$emit('onLabelRemove', labelId); this.$emit('onLabelRemove', labelId);
}, },
handleCollapsedClick() {
this.$emit('onCollapsedValueClick');
},
}, },
}; };
</script> </script>
...@@ -57,16 +82,30 @@ export default { ...@@ -57,16 +82,30 @@ export default {
:class="{ :class="{
'has-labels': selectedLabels.length, 'has-labels': selectedLabels.length,
}" }"
class="hide-collapsed value issuable-show-labels js-value" class="value issuable-show-labels js-value"
data-testid="value-wrapper" data-testid="value-wrapper"
> >
<span v-if="!selectedLabels.length" class="text-secondary" data-testid="empty-placeholder"> <div
v-gl-tooltip.left.viewport
:title="labelsList"
class="sidebar-collapsed-icon"
@click="handleCollapsedClick"
>
<gl-icon name="labels" />
<span class="gl-font-base gl-line-height-24">{{ selectedLabels.length }}</span>
</div>
<span
v-if="!selectedLabels.length"
class="text-secondary hide-collapsed"
data-testid="empty-placeholder"
>
<slot></slot> <slot></slot>
</span> </span>
<template v-else> <template v-else>
<gl-label <gl-label
v-for="label in sortedSelectedLabels" v-for="label in sortedSelectedLabels"
:key="label.id" :key="label.id"
class="hide-collapsed"
data-qa-selector="selected_label_content" data-qa-selector="selected_label_content"
:data-qa-label-name="label.title" :data-qa-label-name="label.title"
:title="label.title" :title="label.title"
......
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
export default {
directives: {
GlTooltip: GlTooltipDirective,
},
components: {
GlIcon,
},
props: {
labels: {
type: Array,
required: true,
},
},
computed: {
labelsList() {
const labelsString = this.labels.length
? this.labels
.slice(0, 5)
.map((label) => label.title)
.join(', ')
: s__('LabelSelect|Labels');
if (this.labels.length > 5) {
return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), {
labelsString,
remainingLabelCount: this.labels.length - 5,
});
}
return labelsString;
},
},
methods: {
handleClick() {
this.$emit('onValueClick');
},
},
};
</script>
<template>
<div
v-gl-tooltip.left.viewport
:title="labelsList"
class="sidebar-collapsed-icon"
@click="handleClick"
>
<gl-icon name="labels" />
<span>{{ labels.length }}</span>
</div>
</template>
...@@ -9,7 +9,6 @@ import { issuableLabelsQueries } from '~/sidebar/constants'; ...@@ -9,7 +9,6 @@ import { issuableLabelsQueries } from '~/sidebar/constants';
import { DEBOUNCE_DROPDOWN_DELAY, DropdownVariant } from './constants'; import { DEBOUNCE_DROPDOWN_DELAY, DropdownVariant } from './constants';
import DropdownContents from './dropdown_contents.vue'; import DropdownContents from './dropdown_contents.vue';
import DropdownValue from './dropdown_value.vue'; import DropdownValue from './dropdown_value.vue';
import DropdownValueCollapsed from './dropdown_value_collapsed.vue';
import { import {
isDropdownVariantSidebar, isDropdownVariantSidebar,
isDropdownVariantStandalone, isDropdownVariantStandalone,
...@@ -20,7 +19,6 @@ export default { ...@@ -20,7 +19,6 @@ export default {
components: { components: {
DropdownValue, DropdownValue,
DropdownContents, DropdownContents,
DropdownValueCollapsed,
SidebarEditableItem, SidebarEditableItem,
}, },
inject: { inject: {
...@@ -294,11 +292,6 @@ export default { ...@@ -294,11 +292,6 @@ export default {
data-qa-selector="labels_block" data-qa-selector="labels_block"
> >
<template v-if="isDropdownVariantSidebar(variant)"> <template v-if="isDropdownVariantSidebar(variant)">
<dropdown-value-collapsed
ref="dropdownButtonCollapsed"
:labels="issuableLabels"
@onValueClick="handleCollapsedValueClick"
/>
<sidebar-editable-item <sidebar-editable-item
ref="editable" ref="editable"
:title="__('Labels')" :title="__('Labels')"
...@@ -314,6 +307,7 @@ export default { ...@@ -314,6 +307,7 @@ export default {
:labels-filter-base-path="labelsFilterBasePath" :labels-filter-base-path="labelsFilterBasePath"
:labels-filter-param="labelsFilterParam" :labels-filter-param="labelsFilterParam"
@onLabelRemove="handleLabelRemove" @onLabelRemove="handleLabelRemove"
@onCollapsedValueClick="handleCollapsedValueClick"
> >
<slot></slot> <slot></slot>
</dropdown-value> </dropdown-value>
......
...@@ -95,5 +95,10 @@ describe('DropdownValue', () => { ...@@ -95,5 +95,10 @@ describe('DropdownValue', () => {
findRegularLabel().vm.$emit('close'); findRegularLabel().vm.$emit('close');
expect(wrapper.emitted('onLabelRemove')).toEqual([[mockRegularLabel.id]]); expect(wrapper.emitted('onLabelRemove')).toEqual([[mockRegularLabel.id]]);
}); });
it('emits `onCollapsedValueClick` when clicking on collapsed value', () => {
wrapper.find('.sidebar-collapsed-icon').trigger('click');
expect(wrapper.emitted('onCollapsedValueClick')).toEqual([[]]);
});
}); });
}); });
...@@ -8,7 +8,6 @@ import { IssuableType } from '~/issues/constants'; ...@@ -8,7 +8,6 @@ import { IssuableType } from '~/issues/constants';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'; import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue'; import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue';
import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue'; import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue';
import DropdownValueCollapsed from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue';
import issueLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql'; import issueLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql';
import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql'; import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
import updateMergeRequestLabelsMutation from '~/sidebar/queries/update_merge_request_labels.mutation.graphql'; import updateMergeRequestLabelsMutation from '~/sidebar/queries/update_merge_request_labels.mutation.graphql';
...@@ -35,7 +34,6 @@ describe('LabelsSelectRoot', () => { ...@@ -35,7 +34,6 @@ describe('LabelsSelectRoot', () => {
const findSidebarEditableItem = () => wrapper.findComponent(SidebarEditableItem); const findSidebarEditableItem = () => wrapper.findComponent(SidebarEditableItem);
const findDropdownValue = () => wrapper.findComponent(DropdownValue); const findDropdownValue = () => wrapper.findComponent(DropdownValue);
const findDropdownValueCollapsed = () => wrapper.findComponent(DropdownValueCollapsed);
const findDropdownContents = () => wrapper.findComponent(DropdownContents); const findDropdownContents = () => wrapper.findComponent(DropdownContents);
const createComponent = ({ const createComponent = ({
...@@ -122,9 +120,6 @@ describe('LabelsSelectRoot', () => { ...@@ -122,9 +120,6 @@ describe('LabelsSelectRoot', () => {
expect(findDropdownValue().props('selectedLabels')).toEqual( expect(findDropdownValue().props('selectedLabels')).toEqual(
issuableLabelsQueryResponse.data.workspace.issuable.labels.nodes, issuableLabelsQueryResponse.data.workspace.issuable.labels.nodes,
); );
expect(findDropdownValueCollapsed().props('labels')).toEqual(
issuableLabelsQueryResponse.data.workspace.issuable.labels.nodes,
);
}); });
it('emits `onLabelRemove` event on dropdown value label remove event', () => { it('emits `onLabelRemove` event on dropdown value label remove event', () => {
......
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