Commit 1e18461b authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '322547-add-user-popup-to-ci-header' into 'master'

Add user popup to CI pipeline header

See merge request gitlab-org/gitlab!60841
parents 366d0e49 ddb4eeff
......@@ -18,8 +18,11 @@ query getPipelineHeaderData($fullPath: ID!, $iid: ID!) {
}
createdAt
user {
id
name
username
webPath
webUrl
email
avatarUrl
status {
......
<script>
import { GlTooltipDirective, GlLink, GlButton, GlTooltip, GlSafeHtmlDirective } from '@gitlab/ui';
import {
GlTooltipDirective,
GlButton,
GlSafeHtmlDirective,
GlAvatarLink,
GlAvatarLabeled,
} from '@gitlab/ui';
import { isGid, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { glEmojiTag } from '../../emoji';
import { __, sprintf } from '../../locale';
import CiIconBadge from './ci_badge_link.vue';
import TimeagoTooltip from './time_ago_tooltip.vue';
import UserAvatarImage from './user_avatar/user_avatar_image.vue';
/**
* Renders header component for job and pipeline page based on UI mockups
......@@ -17,10 +23,9 @@ export default {
components: {
CiIconBadge,
TimeagoTooltip,
UserAvatarImage,
GlLink,
GlButton,
GlTooltip,
GlAvatarLink,
GlAvatarLabeled,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -94,6 +99,9 @@ export default {
return this.itemName;
},
userId() {
return isGid(this.user?.id) ? getIdFromGraphQLId(this.user?.id) : this.user?.id;
},
},
methods: {
......@@ -124,24 +132,32 @@ export default {
{{ __('by') }}
<template v-if="user">
<gl-link
v-gl-tooltip
:href="userPath"
:title="user.email"
class="js-user-link commit-committer-link"
<gl-avatar-link
:data-user-id="userId"
:data-username="user.username"
:data-name="user.name"
:href="user.webUrl"
target="_blank"
class="js-user-link gl-vertical-align-middle gl-mx-2 gl-align-items-center"
>
<user-avatar-image :img-src="avatarUrl" :img-alt="userAvatarAltText" :size="24" />
{{ user.name }}
</gl-link>
<gl-tooltip v-if="message" :target="() => $refs[$options.EMOJI_REF]">
{{ message }}
</gl-tooltip>
<span
v-if="statusTooltipHTML"
:ref="$options.EMOJI_REF"
v-safe-html:[$options.safeHtmlConfig]="statusTooltipHTML"
:data-testid="message"
></span>
<gl-avatar-labeled
:size="24"
:src="avatarUrl"
:label="user.name"
class="gl-display-none gl-sm-display-inline-flex gl-mx-1"
/>
<strong class="author gl-display-inline gl-sm-display-none!">@{{ user.username }}</strong>
<gl-tooltip v-if="message" :target="() => $refs[$options.EMOJI_REF]">
{{ message }}
</gl-tooltip>
<span
v-if="statusTooltipHTML"
:ref="$options.EMOJI_REF"
v-safe-html:[$options.safeHtmlConfig]="statusTooltipHTML"
class="gl-ml-2"
:data-testid="message"
></span>
</gl-avatar-link>
</template>
</section>
......
......@@ -635,7 +635,7 @@ RSpec.describe 'Pipelines', :js do
# header
expect(page).to have_text("##{pipeline.id}")
expect(page).to have_selector(%Q(img[alt$="#{pipeline.user.name}'s avatar"]))
expect(page).to have_selector(%Q(img[src="#{pipeline.user.avatar_url}"]))
expect(page).to have_link(pipeline.user.name, href: user_path(pipeline.user))
# stages
......
import { GlButton, GlLink } from '@gitlab/ui';
import { GlButton, GlAvatarLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import CiIconBadge from '~/vue_shared/components/ci_badge_link.vue';
......@@ -18,6 +18,7 @@ describe('Header CI Component', () => {
},
time: '2017-05-08T14:57:39.781Z',
user: {
id: 1234,
web_url: 'path',
name: 'Foo',
username: 'foobar',
......@@ -29,7 +30,7 @@ describe('Header CI Component', () => {
const findIconBadge = () => wrapper.findComponent(CiIconBadge);
const findTimeAgo = () => wrapper.findComponent(TimeagoTooltip);
const findUserLink = () => wrapper.findComponent(GlLink);
const findUserLink = () => wrapper.findComponent(GlAvatarLink);
const findSidebarToggleBtn = () => wrapper.findComponent(GlButton);
const findActionButtons = () => wrapper.findByTestId('ci-header-action-buttons');
const findHeaderItemText = () => wrapper.findByTestId('ci-header-item-text');
......@@ -64,10 +65,6 @@ describe('Header CI Component', () => {
expect(findTimeAgo().exists()).toBe(true);
});
it('should render user icon and name', () => {
expect(findUserLink().text()).toContain(defaultProps.user.name);
});
it('should render sidebar toggle button', () => {
expect(findSidebarToggleBtn().exists()).toBe(true);
});
......@@ -77,6 +74,45 @@ describe('Header CI Component', () => {
});
});
describe('user avatar', () => {
beforeEach(() => {
createComponent({ itemName: 'Pipeline' });
});
it('contains the username', () => {
expect(findUserLink().text()).toContain(defaultProps.user.username);
});
it('has the correct data attributes', () => {
expect(findUserLink().attributes()).toMatchObject({
'data-user-id': defaultProps.user.id.toString(),
'data-username': defaultProps.user.username,
'data-name': defaultProps.user.name,
});
});
describe('with data from GraphQL', () => {
const userId = 1;
beforeEach(() => {
createComponent({
itemName: 'Pipeline',
user: { ...defaultProps.user, id: `gid://gitlab/User/${1}` },
});
});
it('has the correct user id', () => {
expect(findUserLink().attributes('data-user-id')).toBe(userId.toString());
});
});
describe('with data from REST', () => {
it('has the correct user id', () => {
expect(findUserLink().attributes('data-user-id')).toBe(defaultProps.user.id.toString());
});
});
});
describe('with item id', () => {
beforeEach(() => {
createComponent({ itemName: 'Pipeline', itemId: '123' });
......
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