Commit 8744311a authored by Luke Duncalfe's avatar Luke Duncalfe

Merge branch '285108-admin-users-avatar-component' into 'master'

Add admin users avatar component

See merge request gitlab-org/gitlab!50834
parents 6927d13a 6b241f92
<script>
import { GlAvatarLink, GlAvatarLabeled, GlBadge } from '@gitlab/ui';
import { USER_AVATAR_SIZE } from '../constants';
export default {
components: {
GlAvatarLink,
GlAvatarLabeled,
GlBadge,
},
props: {
user: {
type: Object,
required: true,
},
adminUserPath: {
type: String,
required: true,
},
},
computed: {
adminUserHref() {
return this.adminUserPath.replace('id', this.user.username);
},
},
USER_AVATAR_SIZE,
};
</script>
<template>
<gl-avatar-link
v-if="user"
class="js-user-link"
:href="adminUserHref"
:data-user-id="user.id"
:data-username="user.username"
>
<gl-avatar-labeled
:size="$options.USER_AVATAR_SIZE"
:src="user.avatarUrl"
:label="user.name"
:sub-label="user.email"
>
<template #meta>
<div v-for="(badge, idx) in user.badges" :key="idx" class="gl-p-1">
<gl-badge class="gl-display-flex!" size="sm" :variant="badge.variant">{{
badge.text
}}</gl-badge>
</div>
</template>
</gl-avatar-labeled>
</gl-avatar-link>
</template>
<script>
import { GlTable } from '@gitlab/ui';
import { __ } from '~/locale';
import UserAvatar from './user_avatar.vue';
const DEFAULT_TH_CLASSES =
'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!';
......@@ -9,6 +10,7 @@ const thWidthClass = (width) => `gl-w-${width}p ${DEFAULT_TH_CLASSES}`;
export default {
components: {
GlTable,
UserAvatar,
},
props: {
users: {
......@@ -58,6 +60,10 @@ export default {
:empty-text="s__('AdminUsers|No users found')"
show-empty
stacked="md"
/>
>
<template #cell(name)="{ item: user }">
<UserAvatar :user="user" :admin-user-path="paths.adminUser" />
</template>
</gl-table>
</div>
</template>
export const USER_AVATAR_SIZE = 32;
......@@ -3,7 +3,7 @@
module UsersHelper
def admin_users_data_attributes(users)
{
users: Admin::UserSerializer.new.represent(users).to_json,
users: Admin::UserSerializer.new.represent(users, { current_user: current_user }).to_json,
paths: admin_users_paths.to_json
}
end
......
import { GlAvatarLink, GlAvatarLabeled, GlBadge } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import AdminUserAvatar from '~/admin/users/components/user_avatar.vue';
import { users, paths } from '../mock_data';
describe('AdminUserAvatar component', () => {
let wrapper;
const user = users[0];
const adminUserPath = paths.adminUser;
const findAvatar = () => wrapper.find(GlAvatarLabeled);
const findAvatarLink = () => wrapper.find(GlAvatarLink);
const findAllBadges = () => wrapper.findAll(GlBadge);
const initComponent = (props = {}) => {
wrapper = mount(AdminUserAvatar, {
propsData: {
user,
adminUserPath,
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('when initialized', () => {
beforeEach(() => {
initComponent();
});
it("links to the user's admin path", () => {
expect(findAvatarLink().attributes()).toMatchObject({
href: adminUserPath.replace('id', user.username),
'data-user-id': user.id.toString(),
'data-username': user.username,
});
});
it("renders the user's name", () => {
expect(findAvatar().props('label')).toBe(user.name);
});
it("renders the user's email", () => {
expect(findAvatar().props('subLabel')).toBe(user.email);
});
it("renders the user's avatar image", () => {
expect(findAvatar().attributes('src')).toBe(user.avatarUrl);
});
it("renders the user's badges", () => {
findAllBadges().wrappers.forEach((badge, idx) => {
expect(badge.text()).toBe(user.badges[idx].text);
expect(badge.props('variant')).toBe(user.badges[idx].variant);
});
});
});
});
......@@ -2,6 +2,7 @@ import { GlTable } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import AdminUsersTable from '~/admin/users/components/users_table.vue';
import AdminUserAvatar from '~/admin/users/components/user_avatar.vue';
import { users, paths } from '../mock_data';
describe('AdminUsersTable component', () => {
......@@ -44,8 +45,12 @@ describe('AdminUsersTable component', () => {
${'projectsCount'} | ${'Projects'}
${'createdAt'} | ${'Created on'}
${'lastActivityOn'} | ${'Last activity'}
`('renders users.$key for $label', ({ key, label }) => {
expect(getCellByLabel(0, label).text()).toBe(`${user[key]}`);
`('renders users.$key in column $label', ({ key, label }) => {
expect(getCellByLabel(0, label).text()).toContain(`${user[key]}`);
});
it('renders an AdminUserAvatar component', () => {
expect(getCellByLabel(0, 'Name').find(AdminUserAvatar).exists()).toBe(true);
});
});
......
......@@ -8,7 +8,10 @@ export const users = [
lastActivityOn: '2020-12-09',
avatarUrl:
'https://secure.gravatar.com/avatar/054f062d8b1a42b123f17e13a173cda8?s=80\\u0026d=identicon',
badges: [],
badges: [
{ text: 'Admin', variant: 'success' },
{ text: "It's you!", variant: null },
],
projectsCount: 0,
actions: [],
},
......
......@@ -337,10 +337,14 @@ RSpec.describe UsersHelper do
describe '#admin_users_data_attributes' do
subject(:data) { helper.admin_users_data_attributes([user]) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
it 'users matches the serialized json' do
entity = double
expect_next_instance_of(Admin::UserSerializer) do |instance|
expect(instance).to receive(:represent).with([user]).and_return(entity)
expect(instance).to receive(:represent).with([user], current_user: user).and_return(entity)
end
expect(entity).to receive(:to_json).and_return("{\"username\":\"admin\"}")
expect(data[:users]).to eq "{\"username\":\"admin\"}"
......
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