Commit 7d5cfbca authored by Phil Hughes's avatar Phil Hughes

Merge branch 'add-work-information-to-user-api' into 'master'

Expose work information on User

See merge request gitlab-org/gitlab!29258
parents bcdeb91d aad737c4
......@@ -38,8 +38,7 @@ const populateUserInfo = user => {
name: userData.name,
location: userData.location,
bio: userData.bio,
organization: userData.organization,
jobTitle: userData.job_title,
workInformation: userData.work_information,
loaded: true,
});
}
......@@ -71,7 +70,7 @@ export default (elements = document.querySelectorAll('.js-user-link')) => {
const user = {
location: null,
bio: null,
organization: null,
workInformation: null,
status: null,
loaded: false,
};
......
<script>
import { GlPopover, GlSkeletonLoading, GlSprintf } from '@gitlab/ui';
import { GlPopover, GlSkeletonLoading } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import UserAvatarImage from '../user_avatar/user_avatar_image.vue';
import { glEmojiTag } from '../../../emoji';
import { s__ } from '~/locale';
import { isString } from 'lodash';
export default {
name: 'UserPopover',
......@@ -12,7 +10,6 @@ export default {
Icon,
GlPopover,
GlSkeletonLoading,
GlSprintf,
UserAvatarImage,
},
props: {
......@@ -49,26 +46,7 @@ export default {
return !this.user.name;
},
workInformationIsLoading() {
return !this.user.loaded && this.workInformation === null;
},
workInformation() {
const { jobTitle, organization } = this.user;
if (organization && jobTitle) {
return {
message: s__('Profile|%{job_title} at %{organization}'),
placeholders: { job_title: jobTitle, organization },
};
} else if (organization) {
return organization;
} else if (jobTitle) {
return jobTitle;
}
return null;
},
workInformationShouldUseSprintf() {
return !isString(this.workInformation);
return !this.user.loaded && this.user.workInformation === null;
},
locationIsLoading() {
return !this.user.loaded && this.user.location === null;
......@@ -98,23 +76,13 @@ export default {
<icon name="profile" class="category-icon flex-shrink-0" />
<span ref="bio" class="ml-1">{{ user.bio }}</span>
</div>
<div v-if="workInformation" class="d-flex mb-1">
<div v-if="user.workInformation" class="d-flex mb-1">
<icon
v-show="!workInformationIsLoading"
name="work"
class="category-icon flex-shrink-0"
/>
<span ref="workInformation" class="ml-1">
<gl-sprintf v-if="workInformationShouldUseSprintf" :message="workInformation.message">
<template
v-for="(placeholder, slotName) in workInformation.placeholders"
v-slot:[slotName]
>
<span :key="slotName">{{ placeholder }}</span>
</template>
</gl-sprintf>
<span v-else>{{ workInformation }}</span>
</span>
<span ref="workInformation" class="ml-1">{{ user.workInformation }}</span>
</div>
<gl-skeleton-loading
v-if="workInformationIsLoading"
......
......@@ -3,8 +3,12 @@
module API
module Entities
class User < UserBasic
include UsersHelper
expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
expose :bio, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title
expose :work_information do |user|
work_information(user)
end
end
end
end
......@@ -10,8 +10,7 @@ const DEFAULT_PROPS = {
name: 'Administrator',
location: 'Vienna',
bio: null,
organization: null,
jobTitle: null,
workInformation: null,
status: null,
},
};
......@@ -59,8 +58,7 @@ describe('User Popover Component', () => {
username: null,
location: null,
bio: null,
organization: null,
jobTitle: null,
workInformation: null,
status: null,
},
},
......@@ -93,7 +91,7 @@ describe('User Popover Component', () => {
const findWorkInformation = () => wrapper.find({ ref: 'workInformation' });
const findBio = () => wrapper.find({ ref: 'bio' });
it('should show only bio if organization and job title are not available', () => {
it('should show only bio if work information is not available', () => {
const user = { ...DEFAULT_PROPS.user, bio: 'My super interesting bio' };
createWrapper({ user });
......@@ -102,27 +100,10 @@ describe('User Popover Component', () => {
expect(findWorkInformation().exists()).toBe(false);
});
it('should show only organization if job title is not available', () => {
const user = { ...DEFAULT_PROPS.user, organization: 'GitLab' };
createWrapper({ user });
expect(findWorkInformation().text()).toBe('GitLab');
});
it('should show only job title if organization is not available', () => {
const user = { ...DEFAULT_PROPS.user, jobTitle: 'Frontend Engineer' };
createWrapper({ user });
expect(findWorkInformation().text()).toBe('Frontend Engineer');
});
it('should show organization and job title if they are both available', () => {
it('should show work information when it is available', () => {
const user = {
...DEFAULT_PROPS.user,
organization: 'GitLab',
jobTitle: 'Frontend Engineer',
workInformation: 'Frontend Engineer at GitLab',
};
createWrapper({ user });
......@@ -130,17 +111,17 @@ describe('User Popover Component', () => {
expect(findWorkInformation().text()).toBe('Frontend Engineer at GitLab');
});
it('should display bio and job info in separate lines', () => {
it('should display bio and work information in separate lines', () => {
const user = {
...DEFAULT_PROPS.user,
bio: 'My super interesting bio',
organization: 'GitLab',
workInformation: 'Frontend Engineer at GitLab',
};
createWrapper({ user });
expect(findBio().text()).toBe('My super interesting bio');
expect(findWorkInformation().text()).toBe('GitLab');
expect(findWorkInformation().text()).toBe('Frontend Engineer at GitLab');
});
it('should not encode special characters in bio', () => {
......@@ -154,40 +135,6 @@ describe('User Popover Component', () => {
expect(findBio().text()).toBe('I like <html> & CSS');
});
it('should not encode special characters in organization', () => {
const user = {
...DEFAULT_PROPS.user,
organization: 'Me & my <funky> Company',
};
createWrapper({ user });
expect(findWorkInformation().text()).toBe('Me & my <funky> Company');
});
it('should not encode special characters in job title', () => {
const user = {
...DEFAULT_PROPS.user,
jobTitle: 'Manager & Team Lead',
};
createWrapper({ user });
expect(findWorkInformation().text()).toBe('Manager & Team Lead');
});
it('should not encode special characters when both job title and organization are set', () => {
const user = {
...DEFAULT_PROPS.user,
jobTitle: 'Manager & Team Lead',
organization: 'Me & my <funky> Company',
};
createWrapper({ user });
expect(findWorkInformation().text()).toBe('Manager & Team Lead at Me & my <funky> Company');
});
it('shows icon for bio', () => {
const user = {
...DEFAULT_PROPS.user,
......@@ -201,10 +148,10 @@ describe('User Popover Component', () => {
);
});
it('shows icon for organization', () => {
it('shows icon for work information', () => {
const user = {
...DEFAULT_PROPS.user,
organization: 'GitLab',
workInformation: 'GitLab',
};
createWrapper({ user });
......
# frozen_string_literal: true
require 'spec_helper'
describe API::Entities::User do
let(:user) { create(:user) }
let(:current_user) { create(:user) }
subject { described_class.new(user, current_user: current_user).as_json }
it 'exposes correct attributes' do
expect(subject).to include(:bio, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title, :work_information)
end
it 'exposes created_at if the current user can read the user profile' do
allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, user).and_return(true)
expect(subject).to include(:created_at)
end
it 'does not expose created_at if the current user cannot read the user profile' do
allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, user).and_return(false)
expect(subject).not_to include(:created_at)
end
end
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