Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
a231a01b
Commit
a231a01b
authored
Mar 10, 2022
by
Olena Horal-Koretska
Committed by
Frédéric Caplette
Mar 10, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Render user avatar image using `GlAvatar`
This is added behind the FF which is introduced in this MR
parent
6a5a8458
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
172 additions
and
55 deletions
+172
-55
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
...s/vue_shared/components/user_avatar/user_avatar_image.vue
+28
-14
config/feature_flags/development/gl_avatar_for_all_user_avatars.yml
...ture_flags/development/gl_avatar_for_all_user_avatars.yml
+8
-0
ee/spec/features/merge_requests/user_resets_approvers_spec.rb
...pec/features/merge_requests/user_resets_approvers_spec.rb
+1
-0
ee/spec/features/projects/settings/merge_request_approvals_settings_spec.rb
...rojects/settings/merge_request_approvals_settings_spec.rb
+1
-0
lib/gitlab/gon_helper.rb
lib/gitlab/gon_helper.rb
+1
-0
spec/features/boards/boards_spec.rb
spec/features/boards/boards_spec.rb
+1
-0
spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
...ures/merge_request/user_sees_avatar_on_diff_notes_spec.rb
+1
-0
spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js
...e_shared/components/user_avatar/user_avatar_image_spec.js
+131
-41
No files found.
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
View file @
a231a01b
...
@@ -6,25 +6,28 @@
...
@@ -6,25 +6,28 @@
Sample configuration:
Sample configuration:
<user-avatar-image
<user-avatar-image
:lazy="true"
lazy
:img-src="userAvatarSrc"
:img-src="userAvatarSrc"
:img-alt="tooltipText"
:img-alt="tooltipText"
:tooltip-text="tooltipText"
:tooltip-text="tooltipText"
tooltip-placement="top"
tooltip-placement="top"
/>
/>
*/
*/
import
{
GlTooltip
}
from
'
@gitlab/ui
'
;
import
{
GlTooltip
,
GlAvatar
}
from
'
@gitlab/ui
'
;
import
defaultAvatarUrl
from
'
images/no_avatar.png
'
;
import
defaultAvatarUrl
from
'
images/no_avatar.png
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
import
glFeatureFlagMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
{
placeholderImage
}
from
'
../../../lazy_loader
'
;
import
{
placeholderImage
}
from
'
../../../lazy_loader
'
;
export
default
{
export
default
{
name
:
'
UserAvatarImage
'
,
name
:
'
UserAvatarImage
'
,
components
:
{
components
:
{
GlTooltip
,
GlTooltip
,
GlAvatar
,
},
},
mixins
:
[
glFeatureFlagMixin
()],
props
:
{
props
:
{
lazy
:
{
lazy
:
{
type
:
Boolean
,
type
:
Boolean
,
...
@@ -85,7 +88,20 @@ export default {
...
@@ -85,7 +88,20 @@ export default {
<
template
>
<
template
>
<span>
<span>
<gl-avatar
v-if=
"glFeatures.glAvatarForAllUserAvatars"
ref=
"userAvatarImage"
:class=
"
{
lazy: lazy,
[cssClasses]: true,
}"
:src="resultantSrcAttribute"
:data-src="sanitizedSource"
:size="size"
:alt="imgAlt"
/>
<img
<img
v-else
ref=
"userAvatarImage"
ref=
"userAvatarImage"
:class=
"
{
:class=
"
{
lazy: lazy,
lazy: lazy,
...
@@ -100,11 +116,9 @@ export default {
...
@@ -100,11 +116,9 @@ export default {
class="avatar"
class="avatar"
/>
/>
<gl-tooltip
<gl-tooltip
v-if=
"tooltipText || $slots.default"
:target=
"() => $refs.userAvatarImage"
:target=
"() => $refs.userAvatarImage"
:placement=
"tooltipPlacement"
:placement=
"tooltipPlacement"
boundary=
"window"
boundary=
"window"
class=
"js-user-avatar-image-tooltip"
>
>
<slot>
{{
tooltipText
}}
</slot>
<slot>
{{
tooltipText
}}
</slot>
</gl-tooltip>
</gl-tooltip>
...
...
config/feature_flags/development/gl_avatar_for_all_user_avatars.yml
0 → 100644
View file @
a231a01b
---
name
:
gl_avatar_for_all_user_avatars
introduced_by_url
:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81437
rollout_issue_url
:
https://gitlab.com/gitlab-org/gitlab/-/issues/353477
milestone
:
'
14.9'
type
:
development
group
:
group::foundations
default_enabled
:
false
ee/spec/features/merge_requests/user_resets_approvers_spec.rb
View file @
a231a01b
...
@@ -17,6 +17,7 @@ RSpec.describe 'Merge Requests > User resets approvers', :js do
...
@@ -17,6 +17,7 @@ RSpec.describe 'Merge Requests > User resets approvers', :js do
before
do
before
do
stub_licensed_features
(
multiple_approval_rules:
true
)
stub_licensed_features
(
multiple_approval_rules:
true
)
stub_feature_flags
(
gl_avatar_for_all_user_avatars:
false
)
project_approvers
.
each
do
|
approver
|
project_approvers
.
each
do
|
approver
|
project
.
add_developer
(
approver
)
project
.
add_developer
(
approver
)
...
...
ee/spec/features/projects/settings/merge_request_approvals_settings_spec.rb
View file @
a231a01b
...
@@ -18,6 +18,7 @@ RSpec.describe 'Project settings > [EE] Merge Request Approvals', :js do
...
@@ -18,6 +18,7 @@ RSpec.describe 'Project settings > [EE] Merge Request Approvals', :js do
project
.
add_maintainer
(
user
)
project
.
add_maintainer
(
user
)
group
.
add_developer
(
user
)
group
.
add_developer
(
user
)
group
.
add_developer
(
group_member
)
group
.
add_developer
(
group_member
)
stub_feature_flags
(
gl_avatar_for_all_user_avatars:
false
)
end
end
it
'adds approver'
do
it
'adds approver'
do
...
...
lib/gitlab/gon_helper.rb
View file @
a231a01b
...
@@ -59,6 +59,7 @@ module Gitlab
...
@@ -59,6 +59,7 @@ module Gitlab
push_frontend_feature_flag
(
:bootstrap_confirmation_modals
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:bootstrap_confirmation_modals
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:sandboxed_mermaid
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:sandboxed_mermaid
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:source_editor_toolbar
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:source_editor_toolbar
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:gl_avatar_for_all_user_avatars
,
default_enabled: :yaml
)
end
end
# Exposes the state of a feature flag to the frontend code.
# Exposes the state of a feature flag to the frontend code.
...
...
spec/features/boards/boards_spec.rb
View file @
a231a01b
...
@@ -23,6 +23,7 @@ RSpec.describe 'Project issue boards', :js do
...
@@ -23,6 +23,7 @@ RSpec.describe 'Project issue boards', :js do
project
.
add_maintainer
(
user2
)
project
.
add_maintainer
(
user2
)
sign_in
(
user
)
sign_in
(
user
)
stub_feature_flags
(
gl_avatar_for_all_user_avatars:
false
)
set_cookie
(
'sidebar_collapsed'
,
'true'
)
set_cookie
(
'sidebar_collapsed'
,
'true'
)
end
end
...
...
spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
View file @
a231a01b
...
@@ -25,6 +25,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
...
@@ -25,6 +25,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
before
do
before
do
project
.
add_maintainer
(
user
)
project
.
add_maintainer
(
user
)
sign_in
user
sign_in
user
stub_feature_flags
(
gl_avatar_for_all_user_avatars:
false
)
set_cookie
(
'sidebar_collapsed'
,
'true'
)
set_cookie
(
'sidebar_collapsed'
,
'true'
)
end
end
...
...
spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js
View file @
a231a01b
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlAvatar
,
GlTooltip
}
from
'
@gitlab/ui
'
;
import
defaultAvatarUrl
from
'
images/no_avatar.png
'
;
import
defaultAvatarUrl
from
'
images/no_avatar.png
'
;
import
{
placeholderImage
}
from
'
~/lazy_loader
'
;
import
{
placeholderImage
}
from
'
~/lazy_loader
'
;
import
UserAvatarImage
from
'
~/vue_shared/components/user_avatar/user_avatar_image.vue
'
;
import
UserAvatarImage
from
'
~/vue_shared/components/user_avatar/user_avatar_image.vue
'
;
jest
.
mock
(
'
images/no_avatar.png
'
,
()
=>
'
default-avatar-url
'
);
jest
.
mock
(
'
images/no_avatar.png
'
,
()
=>
'
default-avatar-url
'
);
const
DEFAULT
_PROPS
=
{
const
PROVIDED
_PROPS
=
{
size
:
99
,
size
:
32
,
imgSrc
:
'
myavatarurl.com
'
,
imgSrc
:
'
myavatarurl.com
'
,
imgAlt
:
'
mydisplayname
'
,
imgAlt
:
'
mydisplayname
'
,
cssClasses
:
'
myextraavatarclass
'
,
cssClasses
:
'
myextraavatarclass
'
,
...
@@ -14,6 +15,10 @@ const DEFAULT_PROPS = {
...
@@ -14,6 +15,10 @@ const DEFAULT_PROPS = {
tooltipPlacement
:
'
bottom
'
,
tooltipPlacement
:
'
bottom
'
,
};
};
const
DEFAULT_PROPS
=
{
size
:
20
,
};
describe
(
'
User Avatar Image Component
'
,
()
=>
{
describe
(
'
User Avatar Image Component
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
...
@@ -21,11 +26,87 @@ describe('User Avatar Image Component', () => {
...
@@ -21,11 +26,87 @@ describe('User Avatar Image Component', () => {
wrapper
.
destroy
();
wrapper
.
destroy
();
});
});
describe
(
'
`glAvatarForAllUserAvatars` feature flag enabled
'
,
()
=>
{
describe
(
'
Initialization
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
shallowMount
(
UserAvatarImage
,
{
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
(
`
${
PROVIDED_PROPS
.
imgSrc
}
?width=
${
PROVIDED_PROPS
.
size
}
`
,
);
expect
(
avatar
.
props
()).
toMatchObject
({
src
:
`
${
PROVIDED_PROPS
.
imgSrc
}
?width=
${
PROVIDED_PROPS
.
size
}
`
,
alt
:
PROVIDED_PROPS
.
imgAlt
,
});
});
it
(
'
should add correct CSS classes
'
,
()
=>
{
const
classes
=
wrapper
.
findComponent
(
GlAvatar
).
classes
();
expect
(
classes
).
toContain
(
PROVIDED_PROPS
.
cssClasses
);
expect
(
classes
).
not
.
toContain
(
'
lazy
'
);
});
});
describe
(
'
Initialization when lazy
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
shallowMount
(
UserAvatarImage
,
{
propsData
:
{
...
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
({
src
:
placeholderImage
,
'
data-src
'
:
`
${
PROVIDED_PROPS
.
imgSrc
}
?width=
${
PROVIDED_PROPS
.
size
}
`
,
});
});
});
describe
(
'
Initialization without src
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
shallowMount
(
UserAvatarImage
);
});
it
(
'
should have default avatar image
'
,
()
=>
{
const
imageElement
=
wrapper
.
find
(
'
img
'
);
expect
(
imageElement
.
attributes
(
'
src
'
)).
toBe
(
`
${
defaultAvatarUrl
}
?width=
${
DEFAULT_PROPS
.
size
}
`
,
);
});
});
});
describe
(
'
`glAvatarForAllUserAvatars` feature flag disabled
'
,
()
=>
{
describe
(
'
Initialization
'
,
()
=>
{
describe
(
'
Initialization
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
wrapper
=
shallowMount
(
UserAvatarImage
,
{
wrapper
=
shallowMount
(
UserAvatarImage
,
{
propsData
:
{
propsData
:
{
...
DEFAULT
_PROPS
,
...
PROVIDED
_PROPS
,
},
},
});
});
});
});
...
@@ -34,14 +115,18 @@ describe('User Avatar Image Component', () => {
...
@@ -34,14 +115,18 @@ describe('User Avatar Image Component', () => {
const
imageElement
=
wrapper
.
find
(
'
img
'
);
const
imageElement
=
wrapper
.
find
(
'
img
'
);
expect
(
imageElement
.
exists
()).
toBe
(
true
);
expect
(
imageElement
.
exists
()).
toBe
(
true
);
expect
(
imageElement
.
attributes
(
'
src
'
)).
toBe
(
`
${
DEFAULT_PROPS
.
imgSrc
}
?width=99`
);
expect
(
imageElement
.
attributes
(
'
src
'
)).
toBe
(
expect
(
imageElement
.
attributes
(
'
data-src
'
)).
toBe
(
`
${
DEFAULT_PROPS
.
imgSrc
}
?width=99`
);
`
${
PROVIDED_PROPS
.
imgSrc
}
?width=
${
PROVIDED_PROPS
.
size
}
`
,
expect
(
imageElement
.
attributes
(
'
alt
'
)).
toBe
(
DEFAULT_PROPS
.
imgAlt
);
);
expect
(
imageElement
.
attributes
(
'
data-src
'
)).
toBe
(
`
${
PROVIDED_PROPS
.
imgSrc
}
?width=
${
PROVIDED_PROPS
.
size
}
`
,
);
expect
(
imageElement
.
attributes
(
'
alt
'
)).
toBe
(
PROVIDED_PROPS
.
imgAlt
);
});
});
it
(
'
should properly render img css
'
,
()
=>
{
it
(
'
should properly render img css
'
,
()
=>
{
const
classes
=
wrapper
.
find
(
'
img
'
).
classes
();
const
classes
=
wrapper
.
find
(
'
img
'
).
classes
();
expect
(
classes
).
toEqual
(
expect
.
arrayContaining
([
'
avatar
'
,
'
s99
'
,
DEFAULT_PROPS
.
cssClasses
])
);
expect
(
classes
).
toEqual
([
'
avatar
'
,
'
s32
'
,
PROVIDED_PROPS
.
cssClasses
]
);
expect
(
classes
).
not
.
toContain
(
'
lazy
'
);
expect
(
classes
).
not
.
toContain
(
'
lazy
'
);
});
});
});
});
...
@@ -50,7 +135,7 @@ describe('User Avatar Image Component', () => {
...
@@ -50,7 +135,7 @@ describe('User Avatar Image Component', () => {
beforeEach
(()
=>
{
beforeEach
(()
=>
{
wrapper
=
shallowMount
(
UserAvatarImage
,
{
wrapper
=
shallowMount
(
UserAvatarImage
,
{
propsData
:
{
propsData
:
{
...
DEFAULT
_PROPS
,
...
PROVIDED
_PROPS
,
lazy
:
true
,
lazy
:
true
,
},
},
});
});
...
@@ -61,7 +146,9 @@ describe('User Avatar Image Component', () => {
...
@@ -61,7 +146,9 @@ describe('User Avatar Image Component', () => {
expect
(
imageElement
.
classes
()).
toContain
(
'
lazy
'
);
expect
(
imageElement
.
classes
()).
toContain
(
'
lazy
'
);
expect
(
imageElement
.
attributes
(
'
src
'
)).
toBe
(
placeholderImage
);
expect
(
imageElement
.
attributes
(
'
src
'
)).
toBe
(
placeholderImage
);
expect
(
imageElement
.
attributes
(
'
data-src
'
)).
toBe
(
`
${
DEFAULT_PROPS
.
imgSrc
}
?width=99`
);
expect
(
imageElement
.
attributes
(
'
data-src
'
)).
toBe
(
`
${
PROVIDED_PROPS
.
imgSrc
}
?width=
${
PROVIDED_PROPS
.
size
}
`
,
);
});
});
});
});
...
@@ -73,12 +160,15 @@ describe('User Avatar Image Component', () => {
...
@@ -73,12 +160,15 @@ describe('User Avatar Image Component', () => {
it
(
'
should have default avatar image
'
,
()
=>
{
it
(
'
should have default avatar image
'
,
()
=>
{
const
imageElement
=
wrapper
.
find
(
'
img
'
);
const
imageElement
=
wrapper
.
find
(
'
img
'
);
expect
(
imageElement
.
attributes
(
'
src
'
)).
toBe
(
`
${
defaultAvatarUrl
}
?width=20`
);
expect
(
imageElement
.
attributes
(
'
src
'
)).
toBe
(
`
${
defaultAvatarUrl
}
?width=
${
DEFAULT_PROPS
.
size
}
`
,
);
});
});
});
});
});
describe
(
'
dynamic tooltip content
'
,
()
=>
{
describe
(
'
dynamic tooltip content
'
,
()
=>
{
const
props
=
DEFAULT
_PROPS
;
const
props
=
PROVIDED
_PROPS
;
const
slots
=
{
const
slots
=
{
default
:
[
'
Action!
'
],
default
:
[
'
Action!
'
],
};
};
...
@@ -91,11 +181,11 @@ describe('User Avatar Image Component', () => {
...
@@ -91,11 +181,11 @@ describe('User Avatar Image Component', () => {
});
});
it
(
'
renders the tooltip slot
'
,
()
=>
{
it
(
'
renders the tooltip slot
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-user-avatar-image-tooltip
'
).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
find
Component
(
GlTooltip
).
exists
()).
toBe
(
true
);
});
});
it
(
'
renders the tooltip content
'
,
()
=>
{
it
(
'
renders the tooltip content
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-user-avatar-image-tooltip
'
).
text
()).
toContain
(
slots
.
default
[
0
]);
expect
(
wrapper
.
find
Component
(
GlTooltip
).
text
()).
toContain
(
slots
.
default
[
0
]);
});
});
it
(
'
does not render tooltip data attributes for on avatar image
'
,
()
=>
{
it
(
'
does not render tooltip data attributes for on avatar image
'
,
()
=>
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment