diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue index 45b53b13a7fa3f3735a2f542e9e90ff30b0cd40b..8dc521317cdbef2dd93f3f39a911af4ed009c86b 100644 --- a/app/assets/javascripts/boards/components/board_card_inner.vue +++ b/app/assets/javascripts/boards/components/board_card_inner.vue @@ -147,6 +147,9 @@ export default { showReferencePath() { return !this.isProjectBoard && this.itemReferencePath; }, + avatarSize() { + return { default: 16, lg: 24 }; + }, }, methods: { ...mapActions(['performSearch', 'setError']), @@ -359,17 +362,17 @@ export default { </span> </span> </div> - <div class="board-card-assignee gl-display-flex"> + <div class="board-card-assignee gl-display-flex gl-gap-3"> <user-avatar-link v-for="assignee in cappedAssignees" :key="assignee.id" :link-href="assigneeUrl(assignee)" :img-alt="avatarUrlTitle(assignee)" :img-src="avatarUrl(assignee)" - :img-size="24" - img-css-classes="avatar gl-mr-0!" + :img-size="avatarSize" class="js-no-trigger" tooltip-placement="bottom" + :enforce-gl-avatar="true" > <span class="js-assignee-tooltip"> <span class="gl-font-weight-bold gl-display-block">{{ __('Assignee') }}</span> diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue index d07f65cf5c103926f04e697154922431ed86cc85..c1e618620d89650043c9e16c8c9d70525479ecf3 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue @@ -50,7 +50,7 @@ export default { default: __('user avatar'), }, size: { - type: Number, + type: [Number, Object], required: false, default: 20, }, @@ -64,12 +64,19 @@ export default { required: false, default: 'top', }, + enforceGlAvatar: { + type: Boolean, + required: false, + }, }, }; </script> <template> - <user-avatar-image-new v-if="glFeatures.glAvatarForAllUserAvatars" v-bind="$props"> + <user-avatar-image-new + v-if="glFeatures.glAvatarForAllUserAvatars || enforceGlAvatar" + v-bind="$props" + > <slot></slot> </user-avatar-image-new> <user-avatar-image-old v-else v-bind="$props"> diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue index 707b0bbec67e77ca6ff7042d8f9ab17798965393..cd6103142926fc4bb111797462effd7f9aa758c1 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue @@ -16,6 +16,7 @@ */ import { GlTooltip, GlAvatar } from '@gitlab/ui'; +import { isObject } from 'lodash'; import defaultAvatarUrl from 'images/no_avatar.png'; import { __ } from '~/locale'; import { placeholderImage } from '~/lazy_loader'; @@ -48,7 +49,7 @@ export default { default: __('user avatar'), }, size: { - type: Number, + type: [Number, Object], required: false, default: 20, }, @@ -71,9 +72,16 @@ export default { let baseSrc = this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc; // Only adds the width to the URL if its not a base64 data image if (!(baseSrc.indexOf('data:') === 0) && !baseSrc.includes('?')) - baseSrc += `?width=${this.size}`; + baseSrc += `?width=${this.maximumSize}`; return baseSrc; }, + maximumSize() { + if (isObject(this.size)) { + return Math.max(...Object.values(this.size)); + } + + return this.size; + }, resultantSrcAttribute() { return this.lazy ? placeholderImage : this.sanitizedSource; }, diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue index 887deff17c9bca415c1f5d63332dd04700602f4e..f80abed4d69d820911a9af549884c55d88960d2d 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue @@ -55,7 +55,7 @@ export default { default: '', }, imgSize: { - type: Number, + type: [Number, Object], required: false, default: 20, }, @@ -74,12 +74,19 @@ export default { required: false, default: '', }, + enforceGlAvatar: { + type: Boolean, + required: false, + }, }, }; </script> <template> - <user-avatar-link-new v-if="glFeatures.glAvatarForAllUserAvatars" v-bind="$props"> + <user-avatar-link-new + v-if="glFeatures.glAvatarForAllUserAvatars || enforceGlAvatar" + v-bind="$props" + > <slot></slot> <template #avatar-badge> <slot name="avatar-badge"></slot> diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_new.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_new.vue index 3b459569274474d4ae7ef13663ad09abb85672ec..83551c689c47448f7deb0474d7cef91b662c558b 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_new.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_new.vue @@ -56,7 +56,7 @@ export default { default: '', }, imgSize: { - type: Number, + type: [Number, Object], required: false, default: 20, }, @@ -75,6 +75,10 @@ export default { required: false, default: '', }, + enforceGlAvatar: { + type: Boolean, + required: false, + }, }, computed: { shouldShowUsername() { @@ -97,6 +101,7 @@ export default { :tooltip-text="avatarTooltipText" :tooltip-placement="tooltipPlacement" :lazy="lazy" + :enforce-gl-avatar="enforceGlAvatar" > <slot></slot> </user-avatar-image> diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue index 60b26d688b266cccce67bd1862a69a26345dc805..9da298ad705e577e318b4dcc61307a11dda6d150 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue @@ -21,7 +21,7 @@ export default { default: 10, }, imgSize: { - type: Number, + type: [Number, Object], required: false, default: 20, }, diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 9ba7f809372119f4050a837b6ee6c7c366304872..f279af90aa38490e5d9303c36a4173c6e6d4afde 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -275,7 +275,7 @@ context 'issue card' do it 'shows assignee' do page.within(find('.board:nth-child(2)')) do - expect(page).to have_selector('.avatar', count: 1) + expect(page).to have_selector('.gl-avatar', count: 1) end end diff --git a/spec/frontend/boards/board_card_inner_spec.js b/spec/frontend/boards/board_card_inner_spec.js index c6de3ee69f3f34e8393dea996d53ce626c52ce9b..985902b4a3b1c62353b3d41f3244d64999cb9bc5 100644 --- a/spec/frontend/boards/board_card_inner_spec.js +++ b/spec/frontend/boards/board_card_inner_spec.js @@ -238,7 +238,7 @@ describe('Board card component', () => { }); it('renders assignee', () => { - expect(wrapper.find('.board-card-assignee .avatar').exists()).toBe(true); + expect(wrapper.find('.board-card-assignee .gl-avatar').exists()).toBe(true); }); it('sets title', () => { @@ -336,7 +336,7 @@ describe('Board card component', () => { }); it('renders all three assignees', () => { - expect(wrapper.findAll('.board-card-assignee .avatar').length).toEqual(3); + expect(wrapper.findAll('.board-card-assignee .gl-avatar').length).toEqual(3); }); describe('more than three assignees', () => { @@ -362,7 +362,7 @@ describe('Board card component', () => { }); it('renders two assignees', () => { - expect(wrapper.findAll('.board-card-assignee .avatar').length).toEqual(2); + expect(wrapper.findAll('.board-card-assignee .gl-avatar').length).toEqual(2); }); it('renders 99+ avatar counter', async () => { diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_new_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_new_spec.js index 5e05b54cb8c25e27714712b7754d8e27639a529e..f87737ca86ad467da5ef8bf6530f26df38f8cb4c 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_new_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_new_spec.js @@ -18,6 +18,8 @@ const PROVIDED_PROPS = { describe('User Avatar Image Component', () => { let wrapper; + const findAvatar = () => wrapper.findComponent(GlAvatar); + afterEach(() => { wrapper.destroy(); }); @@ -28,21 +30,14 @@ describe('User Avatar Image Component', () => { propsData: { ...PROVIDED_PROPS, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, }); }); it('should render `GlAvatar` and provide correct properties to it', () => { - const avatar = wrapper.findComponent(GlAvatar); - - expect(avatar.attributes('data-src')).toBe( + expect(findAvatar().attributes('data-src')).toBe( `${PROVIDED_PROPS.imgSrc}?width=${PROVIDED_PROPS.size}`, ); - expect(avatar.props()).toMatchObject({ + expect(findAvatar().props()).toMatchObject({ src: `${PROVIDED_PROPS.imgSrc}?width=${PROVIDED_PROPS.size}`, alt: PROVIDED_PROPS.imgAlt, size: PROVIDED_PROPS.size, @@ -63,23 +58,28 @@ describe('User Avatar Image Component', () => { ...PROVIDED_PROPS, lazy: true, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, }); }); it('should add lazy attributes', () => { - const avatar = wrapper.findComponent(GlAvatar); - - expect(avatar.classes()).toContain('lazy'); - expect(avatar.attributes()).toMatchObject({ + expect(findAvatar().classes()).toContain('lazy'); + expect(findAvatar().attributes()).toMatchObject({ src: placeholderImage, 'data-src': `${PROVIDED_PROPS.imgSrc}?width=${PROVIDED_PROPS.size}`, }); }); + + it('should use maximum number when size is provided as an object', () => { + wrapper = shallowMount(UserAvatarImage, { + propsData: { + ...PROVIDED_PROPS, + size: { default: 16, md: 64, lg: 24 }, + lazy: true, + }, + }); + + expect(findAvatar().attributes('data-src')).toBe(`${PROVIDED_PROPS.imgSrc}?width=${64}`); + }); }); describe('Initialization without src', () => { @@ -89,18 +89,11 @@ describe('User Avatar Image Component', () => { ...PROVIDED_PROPS, imgSrc: null, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, }); }); it('should have default avatar image', () => { - const avatar = wrapper.findComponent(GlAvatar); - - expect(avatar.props('src')).toBe(`${defaultAvatarUrl}?width=${PROVIDED_PROPS.size}`); + expect(findAvatar().props('src')).toBe(`${defaultAvatarUrl}?width=${PROVIDED_PROPS.size}`); }); }); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js index 75d2a936b347b9872bca70851bce342f96565ab2..6ad2ef226c265d4c97dd7d2a8043e0115a7e314e 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js @@ -15,47 +15,37 @@ const PROVIDED_PROPS = { describe('User Avatar Image Component', () => { let wrapper; - afterEach(() => { - wrapper.destroy(); - }); - - describe('when `glAvatarForAllUserAvatars` feature flag enabled', () => { - beforeEach(() => { - wrapper = shallowMount(UserAvatarImage, { - propsData: { - ...PROVIDED_PROPS, + const createWrapper = (props = {}, { glAvatarForAllUserAvatars } = {}) => { + wrapper = shallowMount(UserAvatarImage, { + propsData: { + ...PROVIDED_PROPS, + ...props, + }, + provide: { + glFeatures: { + glAvatarForAllUserAvatars, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, - }); + }, }); + }; - it('should render `UserAvatarImageNew` component', () => { - expect(wrapper.findComponent(UserAvatarImageNew).exists()).toBe(true); - expect(wrapper.findComponent(UserAvatarImageOld).exists()).toBe(false); - }); + afterEach(() => { + wrapper.destroy(); }); - describe('when `glAvatarForAllUserAvatars` feature flag disabled', () => { - beforeEach(() => { - wrapper = shallowMount(UserAvatarImage, { - propsData: { - ...PROVIDED_PROPS, - }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: false, - }, - }, + describe.each([ + [false, true, true], + [true, false, true], + [true, true, true], + [false, false, false], + ])( + 'when glAvatarForAllUserAvatars=%s and enforceGlAvatar=%s', + (glAvatarForAllUserAvatars, enforceGlAvatar, isUsingNewVersion) => { + it(`will render ${isUsingNewVersion ? 'new' : 'old'} version`, () => { + createWrapper({ enforceGlAvatar }, { glAvatarForAllUserAvatars }); + expect(wrapper.findComponent(UserAvatarImageNew).exists()).toBe(isUsingNewVersion); + expect(wrapper.findComponent(UserAvatarImageOld).exists()).toBe(!isUsingNewVersion); }); - }); - - it('should render `UserAvatarImageOld` component', () => { - expect(wrapper.findComponent(UserAvatarImageNew).exists()).toBe(false); - expect(wrapper.findComponent(UserAvatarImageOld).exists()).toBe(true); - }); - }); + }, + ); }); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_new_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_new_spec.js index 5ba80b31b99169deb7c502fea4fa87548600590e..f485a14cfea4ea73f788d6ea6162d6e50fca0ea8 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_new_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_new_spec.js @@ -54,6 +54,7 @@ describe('User Avatar Link Component', () => { size: defaultProps.imgSize, tooltipPlacement: defaultProps.tooltipPlacement, tooltipText: '', + enforceGlAvatar: false, }); }); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_old_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_old_spec.js index 2d513c46e772e3d351dba16dec81ebd2b8afd487..cf7a1025dbaeb1ce74c6b72cacac786fd2162bae 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_old_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_old_spec.js @@ -54,6 +54,7 @@ describe('User Avatar Link Component', () => { size: defaultProps.imgSize, tooltipPlacement: defaultProps.tooltipPlacement, tooltipText: '', + enforceGlAvatar: false, }); }); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_spec.js index b36b83d1feacdefd408c42445754184101dae553..fd3f59008ec643f973c6c27f9b3b40fe152b2595 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_spec.js @@ -15,47 +15,37 @@ const PROVIDED_PROPS = { describe('User Avatar Link Component', () => { let wrapper; - afterEach(() => { - wrapper.destroy(); - }); - - describe('when `glAvatarForAllUserAvatars` feature flag enabled', () => { - beforeEach(() => { - wrapper = shallowMount(UserAvatarLink, { - propsData: { - ...PROVIDED_PROPS, + const createWrapper = (props = {}, { glAvatarForAllUserAvatars } = {}) => { + wrapper = shallowMount(UserAvatarLink, { + propsData: { + ...PROVIDED_PROPS, + ...props, + }, + provide: { + glFeatures: { + glAvatarForAllUserAvatars, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, - }); + }, }); + }; - it('should render `UserAvatarLinkNew` component', () => { - expect(wrapper.findComponent(UserAvatarLinkNew).exists()).toBe(true); - expect(wrapper.findComponent(UserAvatarLinkOld).exists()).toBe(false); - }); + afterEach(() => { + wrapper.destroy(); }); - describe('when `glAvatarForAllUserAvatars` feature flag disabled', () => { - beforeEach(() => { - wrapper = shallowMount(UserAvatarLink, { - propsData: { - ...PROVIDED_PROPS, - }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: false, - }, - }, + describe.each([ + [false, true, true], + [true, false, true], + [true, true, true], + [false, false, false], + ])( + 'when glAvatarForAllUserAvatars=%s and enforceGlAvatar=%s', + (glAvatarForAllUserAvatars, enforceGlAvatar, isUsingNewVersion) => { + it(`will render ${isUsingNewVersion ? 'new' : 'old'} version`, () => { + createWrapper({ enforceGlAvatar }, { glAvatarForAllUserAvatars }); + expect(wrapper.findComponent(UserAvatarLinkNew).exists()).toBe(isUsingNewVersion); + expect(wrapper.findComponent(UserAvatarLinkOld).exists()).toBe(!isUsingNewVersion); }); - }); - - it('should render `UserAvatarLinkOld` component', () => { - expect(wrapper.findComponent(UserAvatarLinkNew).exists()).toBe(false); - expect(wrapper.findComponent(UserAvatarLinkOld).exists()).toBe(true); - }); - }); + }, + ); });