Commit 66ca0814 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'user-access-role-badge-component' into 'master'

Create UserAccessRoleBadge component

See merge request gitlab-org/gitlab!56061
parents 4ee0f71d a999d8d4
<script> <script>
/* eslint-disable vue/no-v-html */ import {
import { GlLoadingIcon, GlBadge, GlTooltipDirective } from '@gitlab/ui'; GlLoadingIcon,
import { visitUrl } from '../../lib/utils/url_utility'; GlBadge,
import identicon from '../../vue_shared/components/identicon.vue'; GlIcon,
GlTooltipDirective,
GlSafeHtmlDirective,
} from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import identicon from '~/vue_shared/components/identicon.vue';
import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue';
import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '../constants'; import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '../constants';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import itemActions from './item_actions.vue'; import itemActions from './item_actions.vue';
import itemCaret from './item_caret.vue'; import itemCaret from './item_caret.vue';
import itemStats from './item_stats.vue'; import itemStats from './item_stats.vue';
import itemStatsValue from './item_stats_value.vue';
import itemTypeIcon from './item_type_icon.vue'; import itemTypeIcon from './item_type_icon.vue';
export default { export default {
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
SafeHtml: GlSafeHtmlDirective,
}, },
components: { components: {
GlBadge, GlBadge,
GlLoadingIcon, GlLoadingIcon,
GlIcon,
UserAccessRoleBadge,
identicon, identicon,
itemCaret, itemCaret,
itemTypeIcon, itemTypeIcon,
itemStats, itemStats,
itemStatsValue,
itemActions, itemActions,
}, },
props: { props: {
...@@ -91,6 +98,7 @@ export default { ...@@ -91,6 +98,7 @@ export default {
} }
}, },
}, },
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
}; };
</script> </script>
...@@ -140,28 +148,31 @@ export default { ...@@ -140,28 +148,31 @@ export default {
data-testid="group-name" data-testid="group-name"
:href="group.relativePath" :href="group.relativePath"
:title="group.fullName" :title="group.fullName"
class="no-expand gl-mt-3 gl-mr-3 gl-text-gray-900!" class="no-expand gl-mr-3 gl-mt-3 gl-text-gray-900!"
:itemprop="microdata.nameItemprop" :itemprop="microdata.nameItemprop"
>{{ >
{{
// ending bracket must be by closing tag to prevent // ending bracket must be by closing tag to prevent
// link hover text-decoration from over-extending // link hover text-decoration from over-extending
group.name group.name
}}</a }}
> </a>
<item-stats-value <gl-icon
:icon-name="visibilityIcon" v-gl-tooltip.hover.bottom
class="gl-display-inline-flex gl-align-items-center gl-mr-3 gl-mt-3 gl-text-gray-500"
:name="visibilityIcon"
:title="visibilityTooltip" :title="visibilityTooltip"
css-class="item-visibility d-inline-flex align-items-center gl-mt-3 gl-mr-2 text-secondary" data-testid="group-visibility-icon"
/> />
<span v-if="group.permission" class="user-access-role gl-mt-3"> <user-access-role-badge v-if="group.permission" class="gl-mt-3">
{{ group.permission }} {{ group.permission }}
</span> </user-access-role-badge>
</div> </div>
<div v-if="group.description" class="description"> <div v-if="group.description" class="description">
<span <span
v-safe-html:[$options.safeHtmlConfig]="group.description"
:itemprop="microdata.descriptionItemprop" :itemprop="microdata.descriptionItemprop"
data-testid="group-description" data-testid="group-description"
v-html="group.description"
> >
</span> </span>
</div> </div>
......
...@@ -7,6 +7,7 @@ import { deprecatedCreateFlash as flash } from '~/flash'; ...@@ -7,6 +7,7 @@ import { deprecatedCreateFlash as flash } from '~/flash';
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import eventHub from '~/sidebar/event_hub'; import eventHub from '~/sidebar/event_hub';
import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue';
import { splitCamelCase } from '../../lib/utils/text_utility'; import { splitCamelCase } from '../../lib/utils/text_utility';
import ReplyButton from './note_actions/reply_button.vue'; import ReplyButton from './note_actions/reply_button.vue';
...@@ -17,6 +18,7 @@ export default { ...@@ -17,6 +18,7 @@ export default {
ReplyButton, ReplyButton,
GlButton, GlButton,
GlDropdownItem, GlDropdownItem,
UserAccessRoleBadge,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -226,24 +228,30 @@ export default { ...@@ -226,24 +228,30 @@ export default {
<template> <template>
<div class="note-actions"> <div class="note-actions">
<span <user-access-role-badge
v-if="isAuthor" v-if="isAuthor"
class="note-role user-access-role has-tooltip d-none d-md-inline-block" v-gl-tooltip
class="gl-mx-3 d-none d-md-inline-block"
:title="displayAuthorBadgeText" :title="displayAuthorBadgeText"
>{{ __('Author') }}</span
> >
<span {{ __('Author') }}
</user-access-role-badge>
<user-access-role-badge
v-if="accessLevel" v-if="accessLevel"
class="note-role user-access-role has-tooltip" v-gl-tooltip
class="gl-mx-3"
:title="displayMemberBadgeText" :title="displayMemberBadgeText"
>{{ accessLevel }}</span
> >
<span {{ accessLevel }}
</user-access-role-badge>
<user-access-role-badge
v-else-if="isContributor" v-else-if="isContributor"
class="note-role user-access-role has-tooltip" v-gl-tooltip
class="gl-mx-3"
:title="displayContributorBadgeText" :title="displayContributorBadgeText"
>{{ __('Contributor') }}</span
> >
{{ __('Contributor') }}
</user-access-role-badge>
<gl-button <gl-button
v-if="canResolve" v-if="canResolve"
ref="resolveButton" ref="resolveButton"
......
...@@ -11,6 +11,7 @@ import { ...@@ -11,6 +11,7 @@ import {
} from '@gitlab/ui'; } from '@gitlab/ui';
import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '~/groups/constants'; import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '~/groups/constants';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue';
export default { export default {
components: { components: {
...@@ -20,6 +21,7 @@ export default { ...@@ -20,6 +21,7 @@ export default {
GlButton, GlButton,
GlTooltip, GlTooltip,
GlLink, GlLink,
UserAccessRoleBadge,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -72,7 +74,9 @@ export default { ...@@ -72,7 +74,9 @@ export default {
<template> <template>
<li :class="rowClass" class="group-row"> <li :class="rowClass" class="group-row">
<div class="group-row-contents gl-display-flex gl-align-items-center gl-py-3 gl-pr-5"> <div class="group-row-contents gl-display-flex gl-align-items-center gl-py-3 gl-pr-5">
<div class="folder-toggle-wrap gl-mr-2 gl-display-flex gl-align-items-center"> <div
class="folder-toggle-wrap gl-mr-3 gl-display-flex gl-align-items-center gl-text-gray-500"
>
<gl-icon name="folder-o" /> <gl-icon name="folder-o" />
</div> </div>
<gl-link <gl-link
...@@ -84,12 +88,12 @@ export default { ...@@ -84,12 +88,12 @@ export default {
<div class="gl-min-w-0 gl-display-flex gl-flex-grow-1 gl-flex-shrink-1 gl-align-items-center"> <div class="gl-min-w-0 gl-display-flex gl-flex-grow-1 gl-flex-shrink-1 gl-align-items-center">
<div class="gl-min-w-0 gl-flex-grow-1 flex-shrink-1"> <div class="gl-min-w-0 gl-flex-grow-1 flex-shrink-1">
<div class="title gl-display-flex gl-align-items-center gl-flex-wrap gl-mr-3"> <div class="title gl-display-flex gl-align-items-center gl-flex-wrap gl-mr-3">
<gl-link :href="group.relative_path" class="gl-mt-3 gl-mr-3 gl-text-gray-900!">{{ <gl-link :href="group.relative_path" class="gl-mt-3 gl-mr-3 gl-text-gray-900!">
group.full_name {{ group.full_name }}
}}</gl-link> </gl-link>
<gl-icon <gl-icon
v-gl-tooltip.hover.bottom v-gl-tooltip.hover.bottom
class="gl-mr-0 gl-inline-flex gl-mt-3 text-secondary" class="gl-display-inline-flex gl-mt-3 gl-mr-3 gl-text-gray-500"
:name="visibilityIcon" :name="visibilityIcon"
:title="visibilityTooltip" :title="visibilityTooltip"
/> />
...@@ -99,11 +103,11 @@ export default { ...@@ -99,11 +103,11 @@ export default {
class="gl-display-none gl-sm-display-flex gl-mt-3 gl-mr-1" class="gl-display-none gl-sm-display-flex gl-mt-3 gl-mr-1"
>{{ __('pending removal') }}</gl-badge >{{ __('pending removal') }}</gl-badge
> >
<span v-if="group.permission" class="user-access-role gl-mt-3"> <user-access-role-badge v-if="group.permission" class="gl-mt-3">
{{ group.permission }} {{ group.permission }}
</span> </user-access-role-badge>
</div> </div>
<div v-if="group.description" class="description"> <div v-if="group.description" class="description gl-line-height-20">
<span v-safe-html="group.markdown_description"> </span> <span v-safe-html="group.markdown_description"> </span>
</div> </div>
</div> </div>
......
<script>
/**
* This component applies particular styling to GlBadge that isn't
* available in the current GlBadge variants.
* Where possible, prefer one of the supported GlBadge variants.
* Discussion issue: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1247
*/
import { GlBadge } from '@gitlab/ui';
export default {
name: 'UserAccessRoleBadge',
components: {
GlBadge,
},
};
</script>
<template>
<gl-badge class="gl-bg-transparent! gl-inset-border-1-gray-100!">
<slot></slot>
</gl-badge>
</template>
...@@ -519,10 +519,6 @@ table.pipeline-project-metrics tr td { ...@@ -519,10 +519,6 @@ table.pipeline-project-metrics tr td {
margin-left: 25px; margin-left: 25px;
} }
.item-visibility {
margin-right: 0;
}
.last-updated { .last-updated {
position: relative; position: relative;
min-width: 250px; min-width: 250px;
......
---
title: Improve styling of user access role badges
merge_request: 56061
author:
type: changed
...@@ -188,7 +188,7 @@ describe('GroupItemComponent', () => { ...@@ -188,7 +188,7 @@ describe('GroupItemComponent', () => {
}); });
it('should render component template correctly', () => { it('should render component template correctly', () => {
const visibilityIconEl = vm.$el.querySelector('.item-visibility'); const visibilityIconEl = vm.$el.querySelector('[data-testid="group-visibility-icon"]');
expect(vm.$el.getAttribute('id')).toBe('group-55'); expect(vm.$el.getAttribute('id')).toBe('group-55');
expect(vm.$el.classList.contains('group-row')).toBeTruthy(); expect(vm.$el.classList.contains('group-row')).toBeTruthy();
...@@ -209,8 +209,7 @@ describe('GroupItemComponent', () => { ...@@ -209,8 +209,7 @@ describe('GroupItemComponent', () => {
expect(vm.$el.querySelector('.title a.no-expand')).toBeDefined(); expect(vm.$el.querySelector('.title a.no-expand')).toBeDefined();
expect(visibilityIconEl).not.toBe(null); expect(visibilityIconEl).not.toBe(null);
expect(visibilityIconEl.title).toBe(vm.visibilityTooltip); expect(visibilityIconEl.getAttribute('title')).toBe(vm.visibilityTooltip);
expect(visibilityIconEl.querySelectorAll('svg').length).toBeGreaterThan(0);
expect(vm.$el.querySelector('.access-type')).toBeDefined(); expect(vm.$el.querySelector('.access-type')).toBeDefined();
expect(vm.$el.querySelector('.description')).toBeDefined(); expect(vm.$el.querySelector('.description')).toBeDefined();
......
...@@ -6,6 +6,7 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -6,6 +6,7 @@ import axios from '~/lib/utils/axios_utils';
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import noteActions from '~/notes/components/note_actions.vue'; import noteActions from '~/notes/components/note_actions.vue';
import createStore from '~/notes/stores'; import createStore from '~/notes/stores';
import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue';
import { userDataMock } from '../mock_data'; import { userDataMock } from '../mock_data';
describe('noteActions', () => { describe('noteActions', () => {
...@@ -15,6 +16,9 @@ describe('noteActions', () => { ...@@ -15,6 +16,9 @@ describe('noteActions', () => {
let actions; let actions;
let axiosMock; let axiosMock;
const findUserAccessRoleBadge = (idx) => wrapper.findAll(UserAccessRoleBadge).at(idx);
const findUserAccessRoleBadgeText = (idx) => findUserAccessRoleBadge(idx).text().trim();
const mountNoteActions = (propsData, computed) => { const mountNoteActions = (propsData, computed) => {
const localVue = createLocalVue(); const localVue = createLocalVue();
return mount(localVue.extend(noteActions), { return mount(localVue.extend(noteActions), {
...@@ -66,11 +70,11 @@ describe('noteActions', () => { ...@@ -66,11 +70,11 @@ describe('noteActions', () => {
}); });
it('should render noteable author badge', () => { it('should render noteable author badge', () => {
expect(wrapper.findAll('.note-role').at(0).text().trim()).toEqual('Author'); expect(findUserAccessRoleBadgeText(0)).toBe('Author');
}); });
it('should render access level badge', () => { it('should render access level badge', () => {
expect(wrapper.findAll('.note-role').at(1).text().trim()).toEqual(props.accessLevel); expect(findUserAccessRoleBadgeText(1)).toBe(props.accessLevel);
}); });
it('should render contributor badge', () => { it('should render contributor badge', () => {
...@@ -80,7 +84,7 @@ describe('noteActions', () => { ...@@ -80,7 +84,7 @@ describe('noteActions', () => {
}); });
return wrapper.vm.$nextTick().then(() => { return wrapper.vm.$nextTick().then(() => {
expect(wrapper.findAll('.note-role').at(1).text().trim()).toBe('Contributor'); expect(findUserAccessRoleBadgeText(1)).toBe('Contributor');
}); });
}); });
......
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue';
describe('UserAccessRoleBadge', () => {
let wrapper;
const createComponent = ({ slots } = {}) => {
wrapper = shallowMount(UserAccessRoleBadge, {
slots,
});
};
it('renders slot content inside GlBadge', () => {
createComponent({
slots: {
default: 'test slot content',
},
});
const badge = wrapper.find(GlBadge);
expect(badge.exists()).toBe(true);
expect(badge.html()).toContain('test slot content');
});
});
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