Commit 386e7775 authored by Florie Guibert's avatar Florie Guibert Committed by Kushal Pandya

Update labels in Vue with GlLabel component

- Epic sidebar
- Issue boards
- Group Issues list
parent f61daee3
<script>
import _ from 'underscore';
import { mapState } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui';
import { GlLabel, GlTooltipDirective } from '@gitlab/ui';
import issueCardInner from 'ee_else_ce/boards/mixins/issue_card_inner';
import { sprintf, __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
......@@ -10,18 +10,17 @@ import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_
import IssueDueDate from './issue_due_date.vue';
import IssueTimeEstimate from './issue_time_estimate.vue';
import boardsStore from '../stores/boards_store';
import IssueCardInnerScopedLabel from './issue_card_inner_scoped_label.vue';
import { isScopedLabel } from '~/lib/utils/common_utils';
export default {
components: {
GlLabel,
Icon,
UserAvatarLink,
TooltipOnTruncate,
IssueDueDate,
IssueTimeEstimate,
IssueCardWeight: () => import('ee_component/boards/components/issue_card_weight.vue'),
IssueCardInnerScopedLabel,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -145,12 +144,6 @@ export default {
boardsStore.toggleFilter(filter);
},
labelStyle(label) {
return {
backgroundColor: label.color,
color: label.textColor,
};
},
showScopedLabel(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
......@@ -184,27 +177,16 @@ export default {
</div>
<div v-if="showLabelFooter" class="board-card-labels prepend-top-4 d-flex flex-wrap">
<template v-for="label in orderedLabels">
<issue-card-inner-scoped-label
v-if="showScopedLabel(label)"
<gl-label
:key="label.id"
:label="label"
:label-style="labelStyle(label)"
:background-color="label.color"
:title="label.title"
:description="label.description"
size="sm"
:scoped="showScopedLabel(label)"
:scoped-labels-documentation-link="helpLink"
@scoped-label-click="filterByLabel($event)"
/>
<button
v-else
:key="label.id"
v-gl-tooltip
:style="labelStyle(label)"
:title="label.description"
class="badge color-label append-right-4 prepend-top-4"
type="button"
@click="filterByLabel(label)"
>
{{ label.title }}
</button>
/>
</template>
</div>
<div class="board-card-footer d-flex justify-content-between align-items-end">
......
<script>
import { GlLink, GlTooltip } from '@gitlab/ui';
export default {
components: {
GlTooltip,
GlLink,
},
props: {
label: {
type: Object,
required: true,
},
labelStyle: {
type: Object,
required: true,
},
scopedLabelsDocumentationLink: {
type: String,
required: true,
},
},
};
</script>
<template>
<span
class="d-inline-block position-relative scoped-label-wrapper append-right-4 prepend-top-4 board-label"
>
<a @click="$emit('scoped-label-click', label)">
<span :ref="'labelTitleRef'" :style="labelStyle" class="badge label color-label">
{{ label.title }}
</span>
<gl-tooltip :target="() => $refs.labelTitleRef" placement="top" boundary="viewport">
<span class="font-weight-bold scoped-label-tooltip-title">{{ __('Scoped label') }}</span
><br />
{{ label.description }}
</gl-tooltip>
</a>
<gl-link :href="scopedLabelsDocumentationLink" target="_blank" class="label scoped-label"
><i class="fa fa-question-circle" :style="labelStyle"></i
></gl-link>
</span>
</template>
<script>
import DropdownValueScopedLabel from './dropdown_value_scoped_label.vue';
import DropdownValueRegularLabel from './dropdown_value_regular_label.vue';
import { GlLabel } from '@gitlab/ui';
import { isScopedLabel } from '~/lib/utils/common_utils';
export default {
components: {
DropdownValueScopedLabel,
DropdownValueRegularLabel,
GlLabel,
},
props: {
labels: {
......@@ -37,12 +35,6 @@ export default {
labelFilterUrl(label) {
return `${this.labelFilterBasePath}?label_name[]=${encodeURIComponent(label.title)}`;
},
labelStyle(label) {
return {
color: label.textColor,
backgroundColor: label.color,
};
},
scopedLabelsDescription({ description = '' }) {
return `<span class="font-weight-bold scoped-label-tooltip-title">Scoped label</span><br />${description}`;
},
......@@ -65,22 +57,15 @@ export default {
</span>
<template v-for="label in labels" v-else>
<dropdown-value-scoped-label
v-if="showScopedLabels(label)"
<gl-label
:key="label.id"
:label="label"
:label-filter-url="labelFilterUrl(label)"
:label-style="labelStyle(label)"
:target="labelFilterUrl(label)"
:background-color="label.color"
:title="label.title"
:description="label.description"
:scoped="showScopedLabels(label)"
:scoped-labels-documentation-link="scopedLabelsDocumentationLink"
/>
<dropdown-value-regular-label
v-else
:key="label.id"
:label="label"
:label-filter-url="labelFilterUrl(label)"
:label-style="labelStyle(label)"
/>
</template>
</div>
</template>
<script>
import { GlTooltip } from '@gitlab/ui';
export default {
components: {
GlTooltip,
},
props: {
label: {
type: Object,
required: true,
},
labelStyle: {
type: Object,
required: true,
},
labelFilterUrl: {
type: String,
required: true,
},
},
};
</script>
<template>
<a ref="regularLabelRef" :href="labelFilterUrl">
<span :style="labelStyle" class="badge color-label">
{{ label.title }}
</span>
<gl-tooltip
v-if="label.description"
:target="() => $refs.regularLabelRef"
placement="top"
boundary="viewport"
>
{{ label.description }}
</gl-tooltip>
</a>
</template>
<script>
import { GlLink, GlTooltip } from '@gitlab/ui';
export default {
components: {
GlTooltip,
GlLink,
},
props: {
label: {
type: Object,
required: true,
},
labelStyle: {
type: Object,
required: true,
},
scopedLabelsDocumentationLink: {
type: String,
required: true,
},
labelFilterUrl: {
type: String,
required: true,
},
},
};
</script>
<template>
<span class="d-inline-block position-relative scoped-label-wrapper">
<a :href="labelFilterUrl">
<span :ref="`labelTitleRef`" :style="labelStyle" class="badge color-label label">
{{ label.title }}
</span>
<gl-tooltip
v-if="label.description"
:target="() => $refs.labelTitleRef"
placement="top"
boundary="viewport"
>
<span class="font-weight-bold scoped-label-tooltip-title">{{ __('Scoped label') }}</span
><br />
{{ label.description }}
</gl-tooltip>
</a>
<gl-link :href="scopedLabelsDocumentationLink" target="_blank" class="label scoped-label"
><i class="fa fa-question-circle" :style="labelStyle"></i
></gl-link>
</span>
</template>
......@@ -266,20 +266,9 @@
background-color: $blue-50;
}
.badge {
border: 0;
outline: 0;
&:hover {
text-decoration: underline;
}
@include media-breakpoint-down(lg) {
font-size: $gl-font-size-xs;
padding-left: $gl-padding-4;
padding-right: $gl-padding-4;
font-weight: $gl-font-weight-bold;
}
.gl-label {
margin-top: 4px;
margin-right: 4px;
}
.confidential-icon {
......
......@@ -158,6 +158,10 @@
a:not(.btn) {
color: inherit;
.gl-label-text:hover {
color: inherit;
}
&:hover {
color: $blue-800;
......
---
title: Update labels in Vue with GlLabel component
merge_request: 21465
author:
type: changed
......@@ -13,6 +13,7 @@ import { __, n__ } from '~/locale';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
import boardsStoreEE from '../stores/boards_store_ee';
import flash from '~/flash';
import { isScopedLabel } from '~/lib/utils/common_utils';
// NOTE: need to revisit how we handle headerHeight, because we have so many different header and footer options.
export default {
......@@ -157,6 +158,12 @@ export default {
onEnter() {
this.offFocus();
},
showScopedLabels(label) {
return boardsStoreEE.store.scopedLabels.enabled && isScopedLabel(label);
},
helpLink() {
return boardsStoreEE.store.scopedLabels.helpLink;
},
},
};
</script>
......@@ -176,7 +183,7 @@ export default {
<gl-label
:title="activeListLabel.title"
:background-color="activeListLabel.color"
color="light"
:scoped="showScopedLabels(activeListLabel)"
/>
</template>
<template v-else-if="boardListType === $options.assignee">
......
......@@ -90,7 +90,7 @@ export default {
new ListLabel({
id: label.id,
title: label.title,
color: label.color[0],
color: label.color,
textColor: label.text_color,
}),
);
......
......@@ -14,6 +14,8 @@ describe 'Issue Boards', :js do
let!(:stretch) { create(:label, project: project, name: 'Stretch') }
let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], milestone: milestone, labels: [development], weight: 3, relative_position: 2) }
let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
let!(:scoped_label_1) { create(:label, project: project, name: 'Scoped1::Label1') }
let!(:scoped_label_2) { create(:label, project: project, name: 'Scoped2::Label2') }
let(:board) { create(:board, project: project) }
let!(:list) { create(:list, board: board, label: development, position: 0) }
let(:card1) { find('.board:nth-child(2)').find('.board-card:nth-child(2)') }
......@@ -252,9 +254,6 @@ describe 'Issue Boards', :js do
end
context 'scoped labels' do
let!(:scoped_label_1) { create(:label, project: project, name: 'Scoped::Label1') }
let!(:scoped_label_2) { create(:label, project: project, name: 'Scoped::Label2') }
before do
stub_licensed_features(scoped_labels: true)
......@@ -262,7 +261,7 @@ describe 'Issue Boards', :js do
wait_for_requests
end
it 'removes existing scoped label' do
it 'adds multiple scoped labels' do
click_card(card1)
page.within('.labels') do
......@@ -281,16 +280,55 @@ describe 'Issue Boards', :js do
find('.dropdown-menu-close-icon').click
page.within('.value') do
expect(page).to have_selector('.gl-label-scoped', count: 1)
expect(page).not_to have_content(scoped_label_1.scoped_label_value)
expect(page).to have_selector('.gl-label-scoped', count: 2)
expect(page).to have_content(scoped_label_1.scoped_label_key)
expect(page).to have_content(scoped_label_1.scoped_label_value)
expect(page).to have_content(scoped_label_2.scoped_label_key)
expect(page).to have_content(scoped_label_2.scoped_label_value)
end
end
end
context 'with scoped label assigned' do
let!(:issue3) { create(:labeled_issue, project: project, labels: [development, scoped_label_1, scoped_label_2], relative_position: 3) }
let(:board) { create(:board, project: project) }
let(:card3) { find('.board:nth-child(2)').find('.board-card:nth-child(1)') }
before do
stub_licensed_features(scoped_labels: true)
visit project_board_path(project, board)
wait_for_requests
end
it 'removes existing scoped label' do
click_card(card3)
expect(card1).to have_selector('.scoped-label-wrapper', count: 1)
expect(card1).not_to have_content(scoped_label_1.title)
expect(card1).to have_content(scoped_label_2.title)
page.within('.labels') do
click_link 'Edit'
wait_for_requests
click_link scoped_label_2.title
wait_for_requests
find('.dropdown-menu-close-icon').click
page.within('.value') do
expect(page).to have_selector('.gl-label-scoped', count: 1)
expect(page).not_to have_content(scoped_label_1.scoped_label_value)
expect(page).to have_content(scoped_label_2.scoped_label_key)
expect(page).to have_content(scoped_label_2.scoped_label_value)
end
end
expect(card3).to have_selector('.gl-label-scoped', count: 1)
expect(card3).not_to have_content(scoped_label_1.scoped_label_key)
expect(card3).not_to have_content(scoped_label_1.scoped_label_value)
expect(card3).to have_content(scoped_label_2.scoped_label_key)
expect(card3).to have_content(scoped_label_2.scoped_label_value)
end
end
end
end
......@@ -51,7 +51,7 @@ describe 'Labels Hierarchy', :js do
expect(page).to have_selector('a', text: issue.title)
end
expect(page).to have_selector('.badge', text: label.title)
expect(page).to have_selector('.gl-label', text: label.title)
end
end
end
......
......@@ -64,6 +64,10 @@ describe('BoardSettingsSideBar', () => {
findList: bs.findList,
addList: bs.addList,
removeList: bs.removeList,
scopedLabels: {
enabled: false,
scopedLabelsDocumentationLink: '',
},
};
boardsStore.initEESpecific(storeMock);
......
import { mount } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
import IssueCardWeight from 'ee/boards/components/issue_card_weight.vue';
import ListIssueEE from 'ee/boards/models/issue';
import ListLabel from '~/boards/models/label';
import IssueCardInner from '~/boards/components/issue_card_inner.vue';
import defaultStore from '~/boards/stores';
import { GlLabel } from '@gitlab/ui';
describe('Issue card component', () => {
let wrapper;
......@@ -11,7 +12,7 @@ describe('Issue card component', () => {
let list;
const createComponent = (props = {}, store = defaultStore) => {
wrapper = mount(IssueCardInner, {
wrapper = shallowMount(IssueCardInner, {
store,
propsData: {
list,
......@@ -33,7 +34,7 @@ describe('Issue card component', () => {
label: {
id: 5000,
title: 'Testing',
color: 'red',
color: '#ff0000',
description: 'testing;',
textColor: 'white',
},
......@@ -62,7 +63,7 @@ describe('Issue card component', () => {
const label1 = new ListLabel({
id: 3,
title: 'testing 123',
color: 'blue',
color: '#000cff',
text_color: 'white',
description: 'test',
});
......@@ -80,13 +81,14 @@ describe('Issue card component', () => {
id: 9001,
type,
title,
color: '#000000',
}),
);
createComponent({ groupId: 1 });
expect(wrapper.findAll('.badge').length).toBe(3);
expect(wrapper.text()).toContain(title);
expect(wrapper.findAll(GlLabel).length).toBe(3);
expect(wrapper.find(GlLabel).props('title')).toContain(title);
});
it('shows no labels when the isShowingLabels state is false', () => {
......
import Vue from 'vue';
import { mount } from '@vue/test-utils';
import SidebarLabels from 'ee/epic/components/sidebar_items/sidebar_labels.vue';
import createStore from 'ee/epic/store';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import { mockEpicMeta, mockEpicData, mockLabels } from '../../mock_data';
describe('SidebarLabelsComponent', () => {
let vm;
let wrapper;
let store;
beforeEach(() => {
......@@ -16,26 +16,30 @@ describe('SidebarLabelsComponent', () => {
store.dispatch('setEpicMeta', mockEpicMeta);
store.dispatch('setEpicData', mockEpicData);
vm = mountComponentWithStore(Component, {
wrapper = mount(Component, {
propsData: { canUpdate: false, sidebarCollapsed: false },
store,
props: { canUpdate: false, sidebarCollapsed: false },
stubs: {
GlLabel: true,
},
});
});
afterEach(() => {
vm.$destroy();
wrapper.destroy();
wrapper = null;
});
describe('data', () => {
it('returns default data props', () => {
expect(vm.sidebarExpandedOnClick).toBe(false);
expect(wrapper.vm.sidebarExpandedOnClick).toBe(false);
});
});
describe('computed', () => {
describe('epicContext', () => {
it('returns object containing `this.labels` as a child prop', () => {
expect(vm.epicContext.labels).toBe(vm.labels);
expect(wrapper.vm.epicContext.labels).toBe(wrapper.vm.labels);
});
});
});
......@@ -43,11 +47,11 @@ describe('SidebarLabelsComponent', () => {
describe('methods', () => {
describe('toggleSidebarRevealLabelsDropdown', () => {
it('calls `toggleSidebar` action with param `sidebarCollapse`', () => {
jest.spyOn(vm, 'toggleSidebar');
jest.spyOn(wrapper.vm, 'toggleSidebar');
vm.toggleSidebarRevealLabelsDropdown();
wrapper.vm.toggleSidebarRevealLabelsDropdown();
expect(vm.toggleSidebar).toHaveBeenCalledWith(
expect(wrapper.vm.toggleSidebar).toHaveBeenCalledWith(
jasmine.objectContaining({
sidebarCollapsed: false,
}),
......@@ -57,14 +61,14 @@ describe('SidebarLabelsComponent', () => {
describe('handleDropdownClose', () => {
it('calls `toggleSidebar` action only when `sidebarExpandedOnClick` prop is true', () => {
jest.spyOn(vm, 'toggleSidebar');
jest.spyOn(wrapper.vm, 'toggleSidebar');
vm.sidebarExpandedOnClick = true;
wrapper.vm.sidebarExpandedOnClick = true;
vm.handleDropdownClose();
wrapper.vm.handleDropdownClose();
expect(vm.sidebarExpandedOnClick).toBe(false);
expect(vm.toggleSidebar).toHaveBeenCalledWith(
expect(wrapper.vm.sidebarExpandedOnClick).toBe(false);
expect(wrapper.vm.toggleSidebar).toHaveBeenCalledWith(
jasmine.objectContaining({
sidebarCollapsed: false,
}),
......@@ -86,34 +90,34 @@ describe('SidebarLabelsComponent', () => {
it('initializes `epicContext.labels` as empty array when `label.isAny` is `true`', () => {
const labelIsAny = { isAny: true };
vm.handleLabelClick(labelIsAny);
wrapper.vm.handleLabelClick(labelIsAny);
expect(Array.isArray(vm.epicContext.labels)).toBe(true);
expect(vm.epicContext.labels.length).toBe(0);
expect(Array.isArray(wrapper.vm.epicContext.labels)).toBe(true);
expect(wrapper.vm.epicContext.labels.length).toBe(0);
});
it('adds provided `label` to epicContext.labels', () => {
vm.handleLabelClick(label);
wrapper.vm.handleLabelClick(label);
// epicContext.labels gets initialized with initialLabels, hence
// newly insert label will be at second position (index `1`)
expect(vm.epicContext.labels.length).toBe(2);
expect(vm.epicContext.labels[1].id).toBe(label.id);
vm.handleLabelClick(label);
expect(wrapper.vm.epicContext.labels.length).toBe(2);
expect(wrapper.vm.epicContext.labels[1].id).toBe(label.id);
wrapper.vm.handleLabelClick(label);
});
it('filters epicContext.labels to exclude provided `label` if it is already present in `epicContext.labels`', () => {
vm.handleLabelClick(label); // Select
vm.handleLabelClick(label); // Un-select
wrapper.vm.handleLabelClick(label); // Select
wrapper.vm.handleLabelClick(label); // Un-select
expect(vm.epicContext.labels.length).toBe(1);
expect(vm.epicContext.labels[0].id).toBe(mockLabels[0].id);
expect(wrapper.vm.epicContext.labels.length).toBe(1);
expect(wrapper.vm.epicContext.labels[0].id).toBe(mockLabels[0].id);
});
});
});
describe('template', () => {
it('renders labels select element container', () => {
expect(vm.$el.classList.contains('js-labels-block')).toBe(true);
expect(wrapper.vm.$el.classList.contains('js-labels-block')).toBe(true);
});
});
});
......@@ -16983,9 +16983,6 @@ msgstr ""
msgid "Scoped issue boards"
msgstr ""
msgid "Scoped label"
msgstr ""
msgid "Scopes"
msgstr ""
......
......@@ -519,7 +519,7 @@ describe 'Issue Boards', :js do
page.within(find('.board:nth-child(2)')) do
expect(page).to have_selector('.board-card', count: 8)
expect(find('.board-card', match: :first)).to have_content(bug.title)
click_button(bug.title)
click_link(bug.title)
wait_for_requests
end
......@@ -536,7 +536,7 @@ describe 'Issue Boards', :js do
it 'removes label filter by clicking label button on issue' do
page.within(find('.board:nth-child(2)')) do
page.within(find('.board-card', match: :first)) do
click_button(bug.title)
click_link(bug.title)
end
wait_for_requests
......
......@@ -305,7 +305,7 @@ describe 'Issue Boards', :js do
end
# 'Development' label does not show since the card is in a 'Development' list label
expect(card).to have_selector('.badge', count: 2)
expect(card).to have_selector('.gl-label', count: 2)
expect(card).to have_content(bug.title)
end
......@@ -335,7 +335,7 @@ describe 'Issue Boards', :js do
end
# 'Development' label does not show since the card is in a 'Development' list label
expect(card).to have_selector('.badge', count: 3)
expect(card).to have_selector('.gl-label', count: 3)
expect(card).to have_content(bug.title)
expect(card).to have_content(regression.title)
end
......
import { GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import IssueCardInnerScopedLabel from '~/boards/components/issue_card_inner_scoped_label.vue';
describe('IssueCardInnerScopedLabel Component', () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(IssueCardInnerScopedLabel, {
propsData: {
label: { title: 'Foo::Bar', description: 'Some Random Description' },
labelStyle: { background: 'white', color: 'black' },
scopedLabelsDocumentationLink: '/docs-link',
},
});
});
afterEach(() => {
wrapper.destroy();
});
it('should render label title', () => {
expect(wrapper.find('.color-label').text()).toBe('Foo::Bar');
});
it('should render question mark symbol', () => {
expect(wrapper.find('.fa-question-circle').exists()).toBe(true);
});
it('should render label style provided', () => {
const label = wrapper.find('.color-label');
expect(label.attributes('style')).toContain('background: white;');
expect(label.attributes('style')).toContain('color: black;');
});
it('should render the docs link', () => {
expect(wrapper.find(GlLink).attributes('href')).toBe('/docs-link');
});
});
......@@ -8,6 +8,7 @@ import '~/boards/models/list';
import IssueCardInner from '~/boards/components/issue_card_inner.vue';
import { listObj } from '../../javascripts/boards/mock_data';
import store from '~/boards/stores';
import { GlLabel } from '@gitlab/ui';
describe('Issue card component', () => {
const user = new ListAssignee({
......@@ -20,7 +21,7 @@ describe('Issue card component', () => {
const label1 = new ListLabel({
id: 3,
title: 'testing 123',
color: 'blue',
color: '#000CFF',
text_color: 'white',
description: 'test',
});
......@@ -50,6 +51,9 @@ describe('Issue card component', () => {
rootPath: '/',
},
store,
stubs: {
GlLabel: true,
},
});
});
......@@ -290,25 +294,11 @@ describe('Issue card component', () => {
});
it('does not render list label but renders all other labels', () => {
expect(wrapper.findAll('.badge').length).toBe(1);
});
it('renders label', () => {
const nodes = wrapper.findAll('.badge').wrappers.map(label => label.attributes('title'));
expect(nodes.includes(label1.description)).toBe(true);
});
it('sets label description as title', () => {
expect(wrapper.find('.badge').attributes('title')).toContain(label1.description);
});
it('sets background color of button', () => {
const nodes = wrapper
.findAll('.badge')
.wrappers.map(label => label.element.style.backgroundColor);
expect(nodes.includes(label1.color)).toBe(true);
expect(wrapper.findAll(GlLabel).length).toBe(1);
const label = wrapper.find(GlLabel);
expect(label.props('title')).toEqual(label1.title);
expect(label.props('description')).toEqual(label1.description);
expect(label.props('backgroundColor')).toEqual(label1.color);
});
it('does not render label if label does not have an ID', done => {
......@@ -321,7 +311,7 @@ describe('Issue card component', () => {
wrapper.vm
.$nextTick()
.then(() => {
expect(wrapper.findAll('.badge').length).toBe(1);
expect(wrapper.findAll(GlLabel).length).toBe(1);
expect(wrapper.text()).not.toContain('closed');
done();
})
......
import { mount } from '@vue/test-utils';
import { hexToRgb } from '~/lib/utils/color_utils';
import DropdownValueComponent from '~/vue_shared/components/sidebar/labels_select/dropdown_value.vue';
import DropdownValueScopedLabel from '~/vue_shared/components/sidebar/labels_select/dropdown_value_scoped_label.vue';
import { GlLabel } from '@gitlab/ui';
import {
mockConfig,
mockLabels,
} from '../../../../../javascripts/vue_shared/components/sidebar/labels_select/mock_data';
const labelStyles = {
textColor: '#FFFFFF',
color: '#BADA55',
};
const createComponent = (
labels = mockLabels,
labelFilterBasePath = mockConfig.labelFilterBasePath,
) => {
labels.forEach(label => Object.assign(label, labelStyles));
return mount(DropdownValueComponent, {
) =>
mount(DropdownValueComponent, {
propsData: {
labels,
labelFilterBasePath,
enableScopedLabels: true,
},
stubs: {
GlLabel: true,
},
});
};
describe('DropdownValueComponent', () => {
let vm;
......@@ -56,24 +51,17 @@ describe('DropdownValueComponent', () => {
describe('methods', () => {
describe('labelFilterUrl', () => {
it('returns URL string starting with labelFilterBasePath and encoded label.title', () => {
expect(vm.find(DropdownValueScopedLabel).props('labelFilterUrl')).toBe(
'/gitlab-org/my-project/issues?label_name[]=Foo%3A%3ABar',
expect(vm.find(GlLabel).props('target')).toBe(
'/gitlab-org/my-project/issues?label_name[]=Foo%20Label',
);
});
});
describe('labelStyle', () => {
it('returns object with `color` & `backgroundColor` properties from label.textColor & label.color', () => {
expect(vm.find(DropdownValueScopedLabel).props('labelStyle')).toEqual({
color: labelStyles.textColor,
backgroundColor: labelStyles.color,
});
});
});
describe('showScopedLabels', () => {
it('returns true if the label is scoped label', () => {
expect(vm.findAll(DropdownValueScopedLabel).length).toEqual(1);
const labels = vm.findAll(GlLabel);
expect(labels.length).toEqual(2);
expect(labels.at(1).props('scoped')).toBe(true);
});
});
});
......@@ -95,33 +83,10 @@ describe('DropdownValueComponent', () => {
vmEmptyLabels.destroy();
});
it('renders label element with filter URL', () => {
expect(vm.find('a').attributes('href')).toBe(
'/gitlab-org/my-project/issues?label_name[]=Foo%20Label',
);
});
it('renders label element and styles based on label details', () => {
const labelEl = vm.find('a span.badge.color-label');
it('renders DropdownValueComponent element', () => {
const labelEl = vm.find(GlLabel);
expect(labelEl.exists()).toBe(true);
expect(labelEl.attributes('style')).toContain(
`background-color: rgb(${hexToRgb(labelStyles.color).join(', ')});`,
);
expect(labelEl.text().trim()).toBe(mockLabels[0].title);
});
describe('label is of scoped-label type', () => {
it('renders a scoped-label-wrapper span to incorporate 2 anchors', () => {
expect(vm.find('span.scoped-label-wrapper').exists()).toBe(true);
});
it('renders anchor tag containing question icon', () => {
const anchor = vm.find('.scoped-label-wrapper a.scoped-label');
expect(anchor.exists()).toBe(true);
expect(anchor.find('i.fa-question-circle').exists()).toBe(true);
});
});
});
});
......@@ -32,7 +32,7 @@ describe('Board card', () => {
const label1 = new ListLabel({
id: 3,
title: 'testing 123',
color: 'blue',
color: '#000cff',
text_color: 'white',
description: 'test',
});
......@@ -155,12 +155,6 @@ describe('Board card', () => {
expect(boardsStore.detail.issue).toEqual({});
});
it('does not set detail issue if button is clicked', () => {
triggerEvent('mouseup', vm.$el.querySelector('button'));
expect(boardsStore.detail.issue).toEqual({});
});
it('does not set detail issue if img is clicked', done => {
vm.issue.assignees = [
new ListAssignee({
......
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