Skip to content
Snippets Groups Projects
Commit b5ab1d91 authored by Rajat Jain's avatar Rajat Jain Committed by Kushal Pandya
Browse files

Display scoped labels in Issue Boards

This change brings new Scoped labels to Issue board as well.
With the last change, this was missed.
parent d83eb63b
No related branches found
No related tags found
No related merge requests found
Showing
with 181 additions and 24 deletions
Loading
Loading
@@ -16,6 +16,7 @@ import TimeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
import MilestoneSelect from '~/milestone_select';
import RemoveBtn from './sidebar/remove_issue.vue';
import boardsStore from '../stores/boards_store';
import { isScopedLabel } from '~/lib/utils/common_utils';
 
export default Vue.extend({
components: {
Loading
Loading
@@ -140,5 +141,11 @@ export default Vue.extend({
Flash(__('An error occurred while saving assignees'));
});
},
showScopedLabels(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
});
Loading
Loading
@@ -9,6 +9,8 @@ import eventHub from '../eventhub';
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: {
Loading
Loading
@@ -17,6 +19,7 @@ export default {
TooltipOnTruncate,
IssueDueDate,
IssueTimeEstimate,
IssueCardInnerScopedLabel,
},
directives: {
GlTooltip: GlTooltipDirective,
Loading
Loading
@@ -96,6 +99,9 @@ export default {
orderedLabels() {
return _.sortBy(this.issue.labels, 'title');
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
methods: {
isIndexLessThanlimit(index) {
Loading
Loading
@@ -159,6 +165,9 @@ export default {
color: label.textColor,
};
},
showScopedLabel(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
},
};
</script>
Loading
Loading
@@ -179,19 +188,29 @@ export default {
</h4>
</div>
<div v-if="showLabelFooter" class="board-card-labels prepend-top-4 d-flex flex-wrap">
<button
v-for="label in orderedLabels"
v-if="showLabel(label)"
: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 v-for="label in orderedLabels" v-if="showLabel(label)">
<issue-card-inner-scoped-label
v-if="showScopedLabel(label)"
:key="label.id"
:label="label"
:label-style="labelStyle(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">
<div
Loading
Loading
<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>
Loading
Loading
@@ -5,7 +5,7 @@
 
import Vue from 'vue';
import '~/vue_shared/models/label';
import { isEE } from '~/lib/utils/common_utils';
import { isEE, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import IssueProject from './project';
import boardsStore from '../stores/boards_store';
 
Loading
Loading
@@ -141,7 +141,7 @@ class ListIssue {
* PATCH the said object.
*/
if (body) {
this.labels = body.labels;
this.labels = convertObjectPropsToCamelCase(body.labels, { deep: true });
}
});
}
Loading
Loading
Loading
Loading
@@ -9,6 +9,10 @@ import { getUrlParamsArray, parseBoolean } from '~/lib/utils/common_utils';
 
const boardsStore = {
disabled: false,
scopedLabels: {
helpLink: '',
enabled: false,
},
filter: {
path: '',
},
Loading
Loading
Loading
Loading
@@ -11,7 +11,7 @@ import CreateLabelDropdown from './create_label';
import flash from './flash';
import ModalStore from './boards/stores/modal_store';
import boardsStore from './boards/stores/boards_store';
import { isEE } from '~/lib/utils/common_utils';
import { isEE, isScopedLabel } from '~/lib/utils/common_utils';
 
export default class LabelsSelect {
constructor(els, options = {}) {
Loading
Loading
@@ -546,8 +546,6 @@ export default class LabelsSelect {
].join(''),
);
 
const isScopedLabel = label => label.title.indexOf('::') !== -1;
const tpl = _.template(
[
'<% _.each(labels, function(label){ %>',
Loading
Loading
Loading
Loading
@@ -724,6 +724,18 @@ export const NavigationType = {
*/
export const isEE = () => window.gon && window.gon.ee;
 
/**
* Checks if the given Label has a special syntax `::` in
* it's title.
*
* Expected Label to be an Object with `title` as a key:
* { title: 'LabelTitle', ...otherProperties };
*
* @param {Object} label
* @returns Boolean
*/
export const isScopedLabel = ({ title = '' }) => title.indexOf('::') !== -1;
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
Loading
Loading
<script>
import DropdownValueScopedLabel from './dropdown_value_scoped_label.vue';
import DropdownValueRegularLabel from './dropdown_value_regular_label.vue';
import { isScopedLabel } from '~/lib/utils/common_utils';
 
export default {
components: {
Loading
Loading
@@ -45,8 +46,8 @@ export default {
scopedLabelsDescription({ description = '' }) {
return `<span class="font-weight-bold scoped-label-tooltip-title">Scoped label</span><br />${description}`;
},
showScopedLabels({ title = '' }) {
return this.enableScopedLabels && title.indexOf('::') !== -1;
showScopedLabels(label) {
return this.enableScopedLabels && isScopedLabel(label);
},
},
};
Loading
Loading
Loading
Loading
@@ -424,6 +424,12 @@
margin: 0;
line-height: $gl-line-height;
}
&.board-label {
.scoped-label {
top: 1px;
}
}
}
 
// Label inside title of Delete Label Modal
Loading
Loading
Loading
Loading
@@ -7,10 +7,17 @@
.value.issuable-show-labels.dont-hide
%span.no-value{ "v-if" => "issue.labels && issue.labels.length === 0" }
= _("None")
%a{ href: "#",
"v-for" => "label in issue.labels" }
.badge.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
{{ label.title }}
%span{ "v-for" => "label in issue.labels" }
%span.d-inline-block.position-relative.scoped-label-wrapper{ "v-if" => "showScopedLabels(label)" }
%a{ href: '#' }
%span.badge.color-label.label{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
{{ label.title }}
%a.label.scoped-label{ ":href" => "helpLink()" }
%i.fa.fa-question-circle{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
%a{ href: "#", "v-else" => true }
.badge.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
{{ label.title }}
- if can_admin_issue?
.selectbox
%input{ type: "hidden",
Loading
Loading
---
title: Display scoped labels in Issue Boards
merge_request: 27164
author:
type: fixed
import Vue from 'vue';
import IssueCardInnerScopedLabel from '~/boards/components/issue_card_inner_scoped_label.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('IssueCardInnerScopedLabel Component', () => {
let vm;
const Component = Vue.extend(IssueCardInnerScopedLabel);
const props = {
label: { title: 'Foo::Bar', description: 'Some Random Description' },
labelStyle: { background: 'white', color: 'black' },
scopedLabelsDocumentationLink: '/docs-link',
};
const createComponent = () => mountComponent(Component, { ...props });
beforeEach(() => {
vm = createComponent();
});
afterEach(() => {
vm.$destroy();
});
it('should render label title', () => {
expect(vm.$el.querySelector('.color-label').textContent.trim()).toEqual('Foo::Bar');
});
it('should render question mark symbol', () => {
expect(vm.$el.querySelector('.fa-question-circle')).not.toBeNull();
});
it('should render label style provided', () => {
const node = vm.$el.querySelector('.color-label');
expect(node.style.background).toEqual(props.labelStyle.background);
expect(node.style.color).toEqual(props.labelStyle.color);
});
it('should render the docs link', () => {
expect(vm.$el.querySelector('a.scoped-label').href).toContain(
props.scopedLabelsDocumentationLink,
);
});
});
Loading
Loading
@@ -894,4 +894,14 @@ describe('common_utils', () => {
expect(commonUtils.isInViewport(el)).toBe(false);
});
});
describe('isScopedLabel', () => {
it('returns true when `::` is present in title', () => {
expect(commonUtils.isScopedLabel({ title: 'foo::bar' })).toBe(true);
});
it('returns false when `::` is not present', () => {
expect(commonUtils.isScopedLabel({ title: 'foobar' })).toBe(false);
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment