Merge branch '40533-groups-tree-updates' into 'master'
Groups tree enhancements Closes #40533 See merge request gitlab-org/gitlab-ce!15980
This commit is contained in:
commit
1e950e3148
17 changed files with 336 additions and 159 deletions
|
@ -77,7 +77,8 @@ export default {
|
|||
class="group-row"
|
||||
>
|
||||
<div
|
||||
class="group-row-contents">
|
||||
class="group-row-contents"
|
||||
:class="{ 'project-row-contents': !isGroup }">
|
||||
<item-actions
|
||||
v-if="isGroup"
|
||||
:group="group"
|
||||
|
@ -97,7 +98,7 @@ export default {
|
|||
/>
|
||||
</div>
|
||||
<div
|
||||
class="avatar-container s40 hidden-xs"
|
||||
class="avatar-container prepend-top-8 prepend-left-5 s24 hidden-xs"
|
||||
:class="{ 'content-loading': group.isChildrenLoading }"
|
||||
>
|
||||
<a
|
||||
|
@ -106,11 +107,12 @@ export default {
|
|||
>
|
||||
<img
|
||||
v-if="hasAvatar"
|
||||
class="avatar s40"
|
||||
class="avatar s24"
|
||||
:src="group.avatarUrl"
|
||||
/>
|
||||
<identicon
|
||||
v-else
|
||||
size-class="s24"
|
||||
:entity-id=group.id
|
||||
:entity-name="group.name"
|
||||
/>
|
||||
|
@ -123,7 +125,7 @@ export default {
|
|||
:href="group.relativePath"
|
||||
:title="group.fullName"
|
||||
class="no-expand"
|
||||
data-placement="top"
|
||||
data-placement="bottom"
|
||||
>{{
|
||||
// ending bracket must be by closing tag to prevent
|
||||
// link hover text-decoration from over-extending
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<script>
|
||||
import { s__ } from '../../locale';
|
||||
import tooltip from '../../vue_shared/directives/tooltip';
|
||||
import modal from '../../vue_shared/components/modal.vue';
|
||||
import { s__ } from '~/locale';
|
||||
import tooltip from '~/vue_shared/directives/tooltip';
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import modal from '~/vue_shared/components/modal.vue';
|
||||
import eventHub from '../event_hub';
|
||||
import { COMMON_STR } from '../constants';
|
||||
import Icon from '../../vue_shared/components/icon.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
icon,
|
||||
modal,
|
||||
},
|
||||
directives: {
|
||||
|
@ -64,10 +64,9 @@ export default {
|
|||
:title="editBtnTitle"
|
||||
:aria-label="editBtnTitle"
|
||||
data-container="body"
|
||||
data-placement="bottom"
|
||||
class="edit-group btn no-expand">
|
||||
<icon
|
||||
name="settings">
|
||||
</icon>
|
||||
<icon name="settings"/>
|
||||
</a>
|
||||
<a
|
||||
v-tooltip
|
||||
|
@ -77,10 +76,9 @@ export default {
|
|||
:title="leaveBtnTitle"
|
||||
:aria-label="leaveBtnTitle"
|
||||
data-container="body"
|
||||
data-placement="bottom"
|
||||
class="leave-group btn no-expand">
|
||||
<i
|
||||
class="fa fa-sign-out"
|
||||
aria-hidden="true"/>
|
||||
<icon name="leave"/>
|
||||
</a>
|
||||
<modal
|
||||
v-show="modalStatus"
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<script>
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
isGroupOpen: {
|
||||
|
@ -7,9 +9,12 @@ export default {
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
icon,
|
||||
},
|
||||
computed: {
|
||||
iconClass() {
|
||||
return this.isGroupOpen ? 'fa-caret-down' : 'fa-caret-right';
|
||||
return this.isGroupOpen ? 'angle-down' : 'angle-right';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -17,9 +22,9 @@ export default {
|
|||
|
||||
<template>
|
||||
<span class="folder-caret">
|
||||
<i
|
||||
:class="iconClass"
|
||||
class="fa"
|
||||
aria-hidden="true"/>
|
||||
<icon
|
||||
:size="12"
|
||||
:name="iconClass"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
<script>
|
||||
import tooltip from '../../vue_shared/directives/tooltip';
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import { ITEM_TYPE, VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE, PROJECT_VISIBILITY_TYPE } from '../constants';
|
||||
import itemStatsValue from './item_stats_value.vue';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
tooltip,
|
||||
components: {
|
||||
icon,
|
||||
timeAgoTooltip,
|
||||
itemStatsValue,
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
|
@ -34,65 +38,47 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="stats">
|
||||
<span
|
||||
v-tooltip
|
||||
<item-stats-value
|
||||
v-if="isGroup"
|
||||
css-class="number-subgroups"
|
||||
icon-name="folder"
|
||||
:title="s__('Subgroups')"
|
||||
class="number-subgroups"
|
||||
data-placement="top"
|
||||
data-container="body">
|
||||
<i
|
||||
class="fa fa-folder"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{item.subgroupCount}}
|
||||
</span>
|
||||
<span
|
||||
v-tooltip
|
||||
:value=item.subgroupCount
|
||||
/>
|
||||
<item-stats-value
|
||||
v-if="isGroup"
|
||||
css-class="number-projects"
|
||||
icon-name="bookmark"
|
||||
:title="s__('Projects')"
|
||||
class="number-projects"
|
||||
data-placement="top"
|
||||
data-container="body">
|
||||
<i
|
||||
class="fa fa-bookmark"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{item.projectCount}}
|
||||
</span>
|
||||
<span
|
||||
v-tooltip
|
||||
:value=item.projectCount
|
||||
/>
|
||||
<item-stats-value
|
||||
v-if="isGroup"
|
||||
css-class="number-users"
|
||||
icon-name="users"
|
||||
:title="s__('Members')"
|
||||
class="number-users"
|
||||
data-placement="top"
|
||||
data-container="body">
|
||||
<i
|
||||
class="fa fa-users"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{item.memberCount}}
|
||||
</span>
|
||||
<span
|
||||
:value=item.memberCount
|
||||
/>
|
||||
<item-stats-value
|
||||
v-if="isProject"
|
||||
class="project-stars">
|
||||
<i
|
||||
class="fa fa-star"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{item.starCount}}
|
||||
</span>
|
||||
<span
|
||||
v-tooltip
|
||||
css-class="project-stars"
|
||||
icon-name="star"
|
||||
:value=item.starCount
|
||||
/>
|
||||
<item-stats-value
|
||||
css-class="item-visibility"
|
||||
tooltip-placement="left"
|
||||
:icon-name="visibilityIcon"
|
||||
:title="visibilityTooltip"
|
||||
data-placement="left"
|
||||
data-container="body"
|
||||
class="item-visibility">
|
||||
<i
|
||||
:class="visibilityIcon"
|
||||
class="fa"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div
|
||||
class="last-updated"
|
||||
v-if="isProject"
|
||||
>
|
||||
<time-ago-tooltip
|
||||
tooltip-placement="bottom"
|
||||
:time="item.updatedAt"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<script>
|
||||
import tooltip from '~/vue_shared/directives/tooltip';
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
cssClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
iconName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
tooltipPlacement: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'bottom',
|
||||
},
|
||||
/**
|
||||
* value could either be number or string
|
||||
* as `memberCount` is always passed as string
|
||||
* while `subgroupCount` & `projectCount`
|
||||
* are always number
|
||||
*/
|
||||
value: {
|
||||
type: [Number, String],
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
components: {
|
||||
icon,
|
||||
},
|
||||
computed: {
|
||||
isValuePresent() {
|
||||
return this.value !== '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
v-tooltip
|
||||
data-container="body"
|
||||
:data-placement="tooltipPlacement"
|
||||
:class="cssClass"
|
||||
:title="title"
|
||||
>
|
||||
<icon :name="iconName"/>
|
||||
<span
|
||||
v-if="isValuePresent"
|
||||
class="stat-value"
|
||||
>
|
||||
{{value}}
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
|
@ -1,7 +1,11 @@
|
|||
<script>
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import { ITEM_TYPE } from '../constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
icon,
|
||||
},
|
||||
props: {
|
||||
itemType: {
|
||||
type: String,
|
||||
|
@ -16,9 +20,9 @@ export default {
|
|||
computed: {
|
||||
iconClass() {
|
||||
if (this.itemType === ITEM_TYPE.GROUP) {
|
||||
return this.isGroupOpen ? 'fa-folder-open' : 'fa-folder';
|
||||
return this.isGroupOpen ? 'folder-open' : 'folder';
|
||||
}
|
||||
return 'fa-bookmark';
|
||||
return 'bookmark';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -26,9 +30,6 @@ export default {
|
|||
|
||||
<template>
|
||||
<span class="item-type-icon">
|
||||
<i
|
||||
:class="iconClass"
|
||||
class="fa"
|
||||
aria-hidden="true"/>
|
||||
<icon :name="iconClass"/>
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
@ -29,7 +29,7 @@ export const PROJECT_VISIBILITY_TYPE = {
|
|||
};
|
||||
|
||||
export const VISIBILITY_TYPE_ICON = {
|
||||
public: 'fa-globe',
|
||||
internal: 'fa-shield',
|
||||
private: 'fa-lock',
|
||||
public: 'earth',
|
||||
internal: 'shield',
|
||||
private: 'lock',
|
||||
};
|
||||
|
|
|
@ -91,6 +91,7 @@ export default class GroupsStore {
|
|||
subgroupCount: rawGroupItem.subgroup_count,
|
||||
memberCount: rawGroupItem.number_users_with_delimiter,
|
||||
starCount: rawGroupItem.star_count,
|
||||
updatedAt: rawGroupItem.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
vertical-align: top;
|
||||
|
||||
&.s16 { font-size: 12px; line-height: 1.33; }
|
||||
&.s24 { font-size: 14px; line-height: 1.8; }
|
||||
&.s24 { font-size: 13px; line-height: 1.8; }
|
||||
&.s26 { font-size: 20px; line-height: 1.33; }
|
||||
&.s32 { font-size: 20px; line-height: 30px; }
|
||||
&.s40 { font-size: 16px; line-height: 38px; }
|
||||
|
|
|
@ -126,10 +126,8 @@ ul.content-list {
|
|||
}
|
||||
|
||||
.description {
|
||||
p {
|
||||
@include str-truncated;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@include str-truncated;
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.controls {
|
||||
|
@ -315,7 +313,7 @@ ul.indent-list {
|
|||
border: 2px solid $white-normal;
|
||||
|
||||
&.identicon {
|
||||
line-height: 30px;
|
||||
line-height: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -349,14 +347,19 @@ ul.indent-list {
|
|||
|
||||
.folder-caret {
|
||||
width: 15px;
|
||||
|
||||
svg {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.item-type-icon {
|
||||
margin-top: 2px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
> .group-row:not(.has-children) {
|
||||
.folder-caret .fa {
|
||||
.folder-caret {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
@ -439,12 +442,61 @@ ul.indent-list {
|
|||
|
||||
.avatar-container > a {
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.has-more-items {
|
||||
display: block;
|
||||
padding: 20px 10px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
position: relative;
|
||||
line-height: 46px;
|
||||
|
||||
> span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 16px;
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
> span:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
margin: 2px 0 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-left: 5px;
|
||||
|
||||
> .btn {
|
||||
margin-right: $btn-xs-side-margin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-row-contents .stats {
|
||||
line-height: inherit;
|
||||
|
||||
> span:first-child {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.item-visibility {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.last-updated {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
min-width: 250px;
|
||||
text-align: right;
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,12 +508,12 @@ ul.indent-list {
|
|||
|
||||
ul.group-list-tree {
|
||||
li.group-row {
|
||||
&.has-description .title {
|
||||
line-height: inherit;
|
||||
> .group-row-contents .title {
|
||||
line-height: $list-text-height;
|
||||
}
|
||||
|
||||
&:not(.has-description) .title {
|
||||
line-height: $list-text-height;
|
||||
&.has-description > .group-row-contents .title {
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6
changelogs/unreleased/40533-groups-tree-updates.yml
Normal file
6
changelogs/unreleased/40533-groups-tree-updates.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Update groups tree to use GitLab SVG icons, add last updated at information
|
||||
for projects
|
||||
merge_request: 15980
|
||||
author:
|
||||
type: changed
|
|
@ -94,22 +94,14 @@ feature 'Dashboard Groups page', :js do
|
|||
end
|
||||
|
||||
it 'can toggle parent group' do
|
||||
# Collapsed by default
|
||||
expect(page).not_to have_selector("#group-#{group.id} .fa-caret-down", count: 1)
|
||||
expect(page).to have_selector("#group-#{group.id} .fa-caret-right")
|
||||
|
||||
# expand
|
||||
click_group_caret(group)
|
||||
|
||||
expect(page).to have_selector("#group-#{group.id} .fa-caret-down")
|
||||
expect(page).not_to have_selector("#group-#{group.id} .fa-caret-right", count: 1)
|
||||
expect(page).to have_selector("#group-#{group.id} #group-#{subgroup.id}")
|
||||
|
||||
# collapse
|
||||
click_group_caret(group)
|
||||
|
||||
expect(page).not_to have_selector("#group-#{group.id} .fa-caret-down", count: 1)
|
||||
expect(page).to have_selector("#group-#{group.id} .fa-caret-right")
|
||||
expect(page).not_to have_selector("#group-#{group.id} #group-#{subgroup.id}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,24 +16,20 @@ describe('ItemCaretComponent', () => {
|
|||
describe('template', () => {
|
||||
it('should render component template correctly', () => {
|
||||
const vm = createComponent();
|
||||
vm.$mount();
|
||||
expect(vm.$el.classList.contains('folder-caret')).toBeTruthy();
|
||||
expect(vm.$el.querySelectorAll('svg').length).toBe(1);
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('should render caret down icon if `isGroupOpen` prop is `true`', () => {
|
||||
const vm = createComponent(true);
|
||||
vm.$mount();
|
||||
expect(vm.$el.querySelectorAll('i.fa.fa-caret-down').length).toBe(1);
|
||||
expect(vm.$el.querySelectorAll('i.fa.fa-caret-right').length).toBe(0);
|
||||
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-down');
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('should render caret right icon if `isGroupOpen` prop is `false`', () => {
|
||||
const vm = createComponent();
|
||||
vm.$mount();
|
||||
expect(vm.$el.querySelectorAll('i.fa.fa-caret-down').length).toBe(0);
|
||||
expect(vm.$el.querySelectorAll('i.fa.fa-caret-right').length).toBe(1);
|
||||
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-right');
|
||||
vm.$destroy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,7 +26,6 @@ describe('ItemStatsComponent', () => {
|
|||
Object.keys(VISIBILITY_TYPE_ICON).forEach((visibility) => {
|
||||
const item = Object.assign({}, mockParentGroupItem, { visibility });
|
||||
const vm = createComponent(item);
|
||||
vm.$mount();
|
||||
expect(vm.visibilityIcon).toBe(VISIBILITY_TYPE_ICON[visibility]);
|
||||
vm.$destroy();
|
||||
});
|
||||
|
@ -41,7 +40,6 @@ describe('ItemStatsComponent', () => {
|
|||
type: ITEM_TYPE.GROUP,
|
||||
});
|
||||
const vm = createComponent(item);
|
||||
vm.$mount();
|
||||
expect(vm.visibilityTooltip).toBe(GROUP_VISIBILITY_TYPE[visibility]);
|
||||
vm.$destroy();
|
||||
});
|
||||
|
@ -54,7 +52,6 @@ describe('ItemStatsComponent', () => {
|
|||
type: ITEM_TYPE.PROJECT,
|
||||
});
|
||||
const vm = createComponent(item);
|
||||
vm.$mount();
|
||||
expect(vm.visibilityTooltip).toBe(PROJECT_VISIBILITY_TYPE[visibility]);
|
||||
vm.$destroy();
|
||||
});
|
||||
|
@ -68,13 +65,11 @@ describe('ItemStatsComponent', () => {
|
|||
|
||||
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT });
|
||||
vm = createComponent(item);
|
||||
vm.$mount();
|
||||
expect(vm.isProject).toBeTruthy();
|
||||
vm.$destroy();
|
||||
|
||||
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
|
||||
vm = createComponent(item);
|
||||
vm.$mount();
|
||||
expect(vm.isProject).toBeFalsy();
|
||||
vm.$destroy();
|
||||
});
|
||||
|
@ -87,13 +82,11 @@ describe('ItemStatsComponent', () => {
|
|||
|
||||
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
|
||||
vm = createComponent(item);
|
||||
vm.$mount();
|
||||
expect(vm.isGroup).toBeTruthy();
|
||||
vm.$destroy();
|
||||
|
||||
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT });
|
||||
vm = createComponent(item);
|
||||
vm.$mount();
|
||||
expect(vm.isGroup).toBeFalsy();
|
||||
vm.$destroy();
|
||||
});
|
||||
|
@ -101,57 +94,37 @@ describe('ItemStatsComponent', () => {
|
|||
});
|
||||
|
||||
describe('template', () => {
|
||||
it('should render component template correctly', () => {
|
||||
it('renders component container element correctly', () => {
|
||||
const vm = createComponent();
|
||||
|
||||
expect(vm.$el.classList.contains('stats')).toBeTruthy();
|
||||
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('renders item visibility icon and tooltip correctly', () => {
|
||||
const vm = createComponent();
|
||||
vm.$mount();
|
||||
|
||||
const visibilityIconEl = vm.$el.querySelector('.item-visibility');
|
||||
expect(vm.$el.classList.contains('.stats')).toBeDefined();
|
||||
expect(visibilityIconEl).toBeDefined();
|
||||
expect(visibilityIconEl).not.toBe(null);
|
||||
expect(visibilityIconEl.dataset.originalTitle).toBe(vm.visibilityTooltip);
|
||||
expect(visibilityIconEl.querySelector('i.fa')).toBeDefined();
|
||||
expect(visibilityIconEl.querySelectorAll('svg').length > 0).toBeTruthy();
|
||||
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('should render stat icons if `item.type` is Group', () => {
|
||||
const item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
|
||||
const vm = createComponent(item);
|
||||
vm.$mount();
|
||||
|
||||
const subgroupIconEl = vm.$el.querySelector('span.number-subgroups');
|
||||
expect(subgroupIconEl).toBeDefined();
|
||||
expect(subgroupIconEl.dataset.originalTitle).toBe('Subgroups');
|
||||
expect(subgroupIconEl.querySelector('i.fa.fa-folder')).toBeDefined();
|
||||
expect(subgroupIconEl.innerText.trim()).toBe(`${vm.item.subgroupCount}`);
|
||||
|
||||
const projectsIconEl = vm.$el.querySelector('span.number-projects');
|
||||
expect(projectsIconEl).toBeDefined();
|
||||
expect(projectsIconEl.dataset.originalTitle).toBe('Projects');
|
||||
expect(projectsIconEl.querySelector('i.fa.fa-bookmark')).toBeDefined();
|
||||
expect(projectsIconEl.innerText.trim()).toBe(`${vm.item.projectCount}`);
|
||||
|
||||
const membersIconEl = vm.$el.querySelector('span.number-users');
|
||||
expect(membersIconEl).toBeDefined();
|
||||
expect(membersIconEl.dataset.originalTitle).toBe('Members');
|
||||
expect(membersIconEl.querySelector('i.fa.fa-users')).toBeDefined();
|
||||
expect(membersIconEl.innerText.trim()).toBe(`${vm.item.memberCount}`);
|
||||
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('should render stat icons if `item.type` is Project', () => {
|
||||
it('renders start count and last updated information for project item correctly', () => {
|
||||
const item = Object.assign({}, mockParentGroupItem, {
|
||||
type: ITEM_TYPE.PROJECT,
|
||||
starCount: 4,
|
||||
});
|
||||
const vm = createComponent(item);
|
||||
vm.$mount();
|
||||
|
||||
const projectStarIconEl = vm.$el.querySelector('.project-stars');
|
||||
expect(projectStarIconEl).toBeDefined();
|
||||
expect(projectStarIconEl.querySelector('i.fa.fa-star')).toBeDefined();
|
||||
expect(projectStarIconEl.innerText.trim()).toBe(`${vm.item.starCount}`);
|
||||
expect(projectStarIconEl).not.toBe(null);
|
||||
expect(projectStarIconEl.querySelectorAll('svg').length > 0).toBeTruthy();
|
||||
expect(projectStarIconEl.querySelectorAll('.stat-value').length > 0).toBeTruthy();
|
||||
expect(vm.$el.querySelectorAll('.last-updated').length > 0).toBeTruthy();
|
||||
|
||||
vm.$destroy();
|
||||
});
|
||||
|
|
81
spec/javascripts/groups/components/item_stats_value_spec.js
Normal file
81
spec/javascripts/groups/components/item_stats_value_spec.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
import Vue from 'vue';
|
||||
|
||||
import itemStatsValueComponent from '~/groups/components/item_stats_value.vue';
|
||||
|
||||
import mountComponent from '../../helpers/vue_mount_component_helper';
|
||||
|
||||
const createComponent = ({ title, cssClass, iconName, tooltipPlacement, value }) => {
|
||||
const Component = Vue.extend(itemStatsValueComponent);
|
||||
|
||||
return mountComponent(Component, {
|
||||
title,
|
||||
cssClass,
|
||||
iconName,
|
||||
tooltipPlacement,
|
||||
value,
|
||||
});
|
||||
};
|
||||
|
||||
describe('ItemStatsValueComponent', () => {
|
||||
describe('computed', () => {
|
||||
let vm;
|
||||
const itemConfig = {
|
||||
title: 'Subgroups',
|
||||
cssClass: 'number-subgroups',
|
||||
iconName: 'folder',
|
||||
tooltipPlacement: 'left',
|
||||
};
|
||||
|
||||
describe('isValuePresent', () => {
|
||||
it('returns true if non-empty `value` is present', () => {
|
||||
vm = createComponent(Object.assign({}, itemConfig, { value: 10 }));
|
||||
expect(vm.isValuePresent).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns false if empty `value` is present', () => {
|
||||
vm = createComponent(itemConfig);
|
||||
expect(vm.isValuePresent).toBeFalsy();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('template', () => {
|
||||
let vm;
|
||||
beforeEach(() => {
|
||||
vm = createComponent({
|
||||
title: 'Subgroups',
|
||||
cssClass: 'number-subgroups',
|
||||
iconName: 'folder',
|
||||
tooltipPlacement: 'left',
|
||||
value: 10,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders component element correctly', () => {
|
||||
expect(vm.$el.classList.contains('number-subgroups')).toBeTruthy();
|
||||
expect(vm.$el.querySelectorAll('svg').length > 0).toBeTruthy();
|
||||
expect(vm.$el.querySelectorAll('.stat-value').length > 0).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders element tooltip correctly', () => {
|
||||
expect(vm.$el.dataset.originalTitle).toBe('Subgroups');
|
||||
expect(vm.$el.dataset.placement).toBe('left');
|
||||
});
|
||||
|
||||
it('renders element icon correctly', () => {
|
||||
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('folder');
|
||||
});
|
||||
|
||||
it('renders value count correctly', () => {
|
||||
expect(vm.$el.querySelector('.stat-value').innerText.trim()).toContain('10');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -28,12 +28,12 @@ describe('ItemTypeIconComponent', () => {
|
|||
|
||||
vm = createComponent(ITEM_TYPE.GROUP, true);
|
||||
vm.$mount();
|
||||
expect(vm.$el.querySelector('i.fa.fa-folder-open')).toBeDefined();
|
||||
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder-open');
|
||||
vm.$destroy();
|
||||
|
||||
vm = createComponent(ITEM_TYPE.GROUP);
|
||||
vm.$mount();
|
||||
expect(vm.$el.querySelector('i.fa.fa-folder')).toBeDefined();
|
||||
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder');
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
|
@ -42,12 +42,12 @@ describe('ItemTypeIconComponent', () => {
|
|||
|
||||
vm = createComponent(ITEM_TYPE.PROJECT);
|
||||
vm.$mount();
|
||||
expect(vm.$el.querySelectorAll('i.fa.fa-bookmark').length).toBe(1);
|
||||
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('bookmark');
|
||||
vm.$destroy();
|
||||
|
||||
vm = createComponent(ITEM_TYPE.GROUP);
|
||||
vm.$mount();
|
||||
expect(vm.$el.querySelectorAll('i.fa.fa-bookmark').length).toBe(0);
|
||||
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).not.toContain('bookmark');
|
||||
vm.$destroy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,9 +18,9 @@ export const PROJECT_VISIBILITY_TYPE = {
|
|||
};
|
||||
|
||||
export const VISIBILITY_TYPE_ICON = {
|
||||
public: 'fa-globe',
|
||||
internal: 'fa-shield',
|
||||
private: 'fa-lock',
|
||||
public: 'earth',
|
||||
internal: 'shield',
|
||||
private: 'lock',
|
||||
};
|
||||
|
||||
export const mockParentGroupItem = {
|
||||
|
@ -46,6 +46,7 @@ export const mockParentGroupItem = {
|
|||
isOpen: true,
|
||||
isChildrenLoading: false,
|
||||
isBeingRemoved: false,
|
||||
updatedAt: '2017-04-09T18:40:39.101Z',
|
||||
};
|
||||
|
||||
export const mockRawChildren = [
|
||||
|
@ -69,6 +70,7 @@ export const mockRawChildren = [
|
|||
subgroup_count: 2,
|
||||
can_leave: false,
|
||||
children: [],
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -96,6 +98,7 @@ export const mockChildren = [
|
|||
isOpen: true,
|
||||
isChildrenLoading: false,
|
||||
isBeingRemoved: false,
|
||||
updatedAt: '2017-04-09T18:40:39.101Z',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -119,6 +122,7 @@ export const mockGroups = [
|
|||
project_count: 2,
|
||||
subgroup_count: 0,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
},
|
||||
{
|
||||
id: 67,
|
||||
|
@ -139,6 +143,7 @@ export const mockGroups = [
|
|||
project_count: 0,
|
||||
subgroup_count: 0,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
},
|
||||
{
|
||||
id: 54,
|
||||
|
@ -159,6 +164,7 @@ export const mockGroups = [
|
|||
project_count: 0,
|
||||
subgroup_count: 1,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
|
@ -179,6 +185,7 @@ export const mockGroups = [
|
|||
project_count: 1,
|
||||
subgroup_count: 0,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
|
@ -199,6 +206,7 @@ export const mockGroups = [
|
|||
project_count: 2,
|
||||
subgroup_count: 0,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
|
@ -219,6 +227,7 @@ export const mockGroups = [
|
|||
project_count: 1,
|
||||
subgroup_count: 0,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
|
@ -239,6 +248,7 @@ export const mockGroups = [
|
|||
project_count: 4,
|
||||
subgroup_count: 0,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -262,6 +272,7 @@ export const mockSearchedGroups = [
|
|||
project_count: 1,
|
||||
subgroup_count: 2,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
children: [
|
||||
{
|
||||
id: 57,
|
||||
|
@ -282,6 +293,7 @@ export const mockSearchedGroups = [
|
|||
project_count: 4,
|
||||
subgroup_count: 2,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
children: [
|
||||
{
|
||||
id: 60,
|
||||
|
@ -302,6 +314,7 @@ export const mockSearchedGroups = [
|
|||
project_count: 0,
|
||||
subgroup_count: 1,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
children: [
|
||||
{
|
||||
id: 61,
|
||||
|
@ -322,6 +335,7 @@ export const mockSearchedGroups = [
|
|||
project_count: 2,
|
||||
subgroup_count: 0,
|
||||
can_leave: false,
|
||||
updated_at: '2017-04-09T18:40:39.101Z',
|
||||
children: [
|
||||
{
|
||||
id: 17,
|
||||
|
@ -336,6 +350,7 @@ export const mockSearchedGroups = [
|
|||
permission: null,
|
||||
edit_path: '/platform/hardware/bsp/kernel/common/v4.4/edit',
|
||||
star_count: 0,
|
||||
updated_at: '2017-09-12T06:37:04.925Z',
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
|
@ -350,6 +365,7 @@ export const mockSearchedGroups = [
|
|||
permission: null,
|
||||
edit_path: '/platform/hardware/bsp/kernel/common/v4.1/edit',
|
||||
star_count: 0,
|
||||
updated_at: '2017-04-09T18:41:03.112Z',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue