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
0
Merge Requests
0
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
Boxiang Sun
gitlab-ce
Commits
3c668fa0
Commit
3c668fa0
authored
Apr 15, 2017
by
Bryce Johnson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Consolidate user avatar Vue logic
parent
a3eabcc2
Changes
27
Show whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
513 additions
and
78 deletions
+513
-78
app/assets/javascripts/boards/components/issue_card_inner.js
app/assets/javascripts/boards/components/issue_card_inner.js
+12
-15
app/assets/javascripts/cycle_analytics/components/stage_code_component.js
...cripts/cycle_analytics/components/stage_code_component.js
+6
-1
app/assets/javascripts/cycle_analytics/components/stage_issue_component.js
...ripts/cycle_analytics/components/stage_issue_component.js
+6
-2
app/assets/javascripts/cycle_analytics/components/stage_plan_component.js
...cripts/cycle_analytics/components/stage_plan_component.js
+6
-3
app/assets/javascripts/cycle_analytics/components/stage_production_component.js
.../cycle_analytics/components/stage_production_component.js
+6
-2
app/assets/javascripts/cycle_analytics/components/stage_review_component.js
...ipts/cycle_analytics/components/stage_review_component.js
+6
-2
app/assets/javascripts/cycle_analytics/components/stage_staging_component.js
...pts/cycle_analytics/components/stage_staging_component.js
+6
-1
app/assets/javascripts/diff_notes/components/diff_note_avatars.js
...ts/javascripts/diff_notes/components/diff_note_avatars.js
+17
-11
app/assets/javascripts/environments/components/environment_item.vue
.../javascripts/environments/components/environment_item.vue
+9
-9
app/assets/javascripts/pipelines/components/pipeline_url.js
app/assets/javascripts/pipelines/components/pipeline_url.js
+11
-11
app/assets/javascripts/vue_shared/components/commit.js
app/assets/javascripts/vue_shared/components/commit.js
+11
-9
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
...s/vue_shared/components/user_avatar/user_avatar_image.vue
+79
-0
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
...ts/vue_shared/components/user_avatar/user_avatar_link.vue
+80
-0
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_size_mixin.js
...e_shared/components/user_avatar/user_avatar_size_mixin.js
+13
-0
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_svg.vue
...pts/vue_shared/components/user_avatar/user_avatar_svg.vue
+42
-0
app/assets/stylesheets/framework/icons.scss
app/assets/stylesheets/framework/icons.scss
+4
-0
app/assets/stylesheets/pages/environments.scss
app/assets/stylesheets/pages/environments.scss
+0
-4
config/webpack.config.js
config/webpack.config.js
+3
-2
spec/features/merge_requests/diff_notes_avatars_spec.rb
spec/features/merge_requests/diff_notes_avatars_spec.rb
+1
-1
spec/javascripts/boards/issue_card_spec.js
spec/javascripts/boards/issue_card_spec.js
+1
-1
spec/javascripts/pipelines/pipeline_url_spec.js
spec/javascripts/pipelines/pipeline_url_spec.js
+1
-1
spec/javascripts/vue_shared/components/commit_spec.js
spec/javascripts/vue_shared/components/commit_spec.js
+1
-1
spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
...scripts/vue_shared/components/pipelines_table_row_spec.js
+2
-2
spec/javascripts/vue_shared/components/user_avatar_image_spec.js
...vascripts/vue_shared/components/user_avatar_image_spec.js
+60
-0
spec/javascripts/vue_shared/components/user_avatar_link_spec.js
...avascripts/vue_shared/components/user_avatar_link_spec.js
+58
-0
spec/javascripts/vue_shared/components/user_avatar_size_mixin_spec.js
...ipts/vue_shared/components/user_avatar_size_mixin_spec.js
+37
-0
spec/javascripts/vue_shared/components/user_avatar_svg_spec.js
...javascripts/vue_shared/components/user_avatar_svg_spec.js
+35
-0
No files found.
app/assets/javascripts/boards/components/issue_card_inner.js
View file @
3c668fa0
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
userAvatarLink
from
'
../../vue_shared/components/user_avatar/user_avatar_link.vue
'
;
import
eventHub
from
'
../eventhub
'
;
import
eventHub
from
'
../eventhub
'
;
const
Store
=
gl
.
issueBoards
.
BoardsStore
;
const
Store
=
gl
.
issueBoards
.
BoardsStore
;
...
@@ -38,6 +39,9 @@ gl.issueBoards.IssueCardInner = Vue.extend({
...
@@ -38,6 +39,9 @@ gl.issueBoards.IssueCardInner = Vue.extend({
maxCounter
:
99
,
maxCounter
:
99
,
};
};
},
},
components
:
{
userAvatarLink
,
},
computed
:
{
computed
:
{
numberOverLimit
()
{
numberOverLimit
()
{
return
this
.
issue
.
assignees
.
length
-
this
.
limitBeforeCounter
;
return
this
.
issue
.
assignees
.
length
-
this
.
limitBeforeCounter
;
...
@@ -146,23 +150,16 @@ gl.issueBoards.IssueCardInner = Vue.extend({
...
@@ -146,23 +150,16 @@ gl.issueBoards.IssueCardInner = Vue.extend({
</span>
</span>
</h4>
</h4>
<div class="card-assignee">
<div class="card-assignee">
<a
<user-avatar-link
class="has-tooltip js-no-trigger"
:href="assigneeUrl(assignee)"
:title="assigneeUrlTitle(assignee)"
v-for="(assignee, index) in issue.assignees"
v-for="(assignee, index) in issue.assignees"
v-if="shouldRenderAssignee(index)"
v-if="shouldRenderAssignee(index)"
data-container="body"
class="js-no-trigger"
data-placement="bottom"
:link-href="assigneeUrl(assignee)"
>
:img-alt="avatarUrlTitle(assignee)"
<img
:img-src="assignee.avatar"
class="avatar avatar-inline s20"
:tooltip-text="assigneeUrlTitle(assignee)"
:src="assignee.avatar"
tooltip-placement="bottom"
width="20"
height="20"
:alt="avatarUrlTitle(assignee)"
/>
/>
</a>
<span
<span
class="avatar-counter has-tooltip"
class="avatar-counter has-tooltip"
:title="assigneeCounterTooltip"
:title="assigneeCounterTooltip"
...
...
app/assets/javascripts/cycle_analytics/components/stage_code_component.js
View file @
3c668fa0
/* eslint-disable no-param-reassign */
/* eslint-disable no-param-reassign */
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
userAvatarImage
from
'
../../vue_shared/components/user_avatar/user_avatar_image.vue
'
;
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
global
.
cycleAnalytics
=
global
.
cycleAnalytics
||
{};
global
.
cycleAnalytics
=
global
.
cycleAnalytics
||
{};
...
@@ -10,6 +11,9 @@ global.cycleAnalytics.StageCodeComponent = Vue.extend({
...
@@ -10,6 +11,9 @@ global.cycleAnalytics.StageCodeComponent = Vue.extend({
items
:
Array
,
items
:
Array
,
stage
:
Object
,
stage
:
Object
,
},
},
components
:
{
userAvatarImage
,
},
template
:
`
template
:
`
<div>
<div>
<div class="events-description">
<div class="events-description">
...
@@ -19,7 +23,8 @@ global.cycleAnalytics.StageCodeComponent = Vue.extend({
...
@@ -19,7 +23,8 @@ global.cycleAnalytics.StageCodeComponent = Vue.extend({
<ul class="stage-event-list">
<ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item">
<li v-for="mergeRequest in items" class="stage-event-item">
<div class="item-details">
<div class="item-details">
<img class="avatar" :src="mergeRequest.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="mergeRequest.author.avatarUrl"/>
<h5 class="item-title merge-merquest-title">
<h5 class="item-title merge-merquest-title">
<a :href="mergeRequest.url">
<a :href="mergeRequest.url">
{{ mergeRequest.title }}
{{ mergeRequest.title }}
...
...
app/assets/javascripts/cycle_analytics/components/stage_issue_component.js
View file @
3c668fa0
/* eslint-disable no-param-reassign */
/* eslint-disable no-param-reassign */
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
userAvatarImage
from
'
../../vue_shared/components/user_avatar/user_avatar_image.vue
'
;
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
global
.
cycleAnalytics
=
global
.
cycleAnalytics
||
{};
global
.
cycleAnalytics
=
global
.
cycleAnalytics
||
{};
...
@@ -10,6 +10,9 @@ global.cycleAnalytics.StageIssueComponent = Vue.extend({
...
@@ -10,6 +10,9 @@ global.cycleAnalytics.StageIssueComponent = Vue.extend({
items
:
Array
,
items
:
Array
,
stage
:
Object
,
stage
:
Object
,
},
},
components
:
{
userAvatarImage
,
},
template
:
`
template
:
`
<div>
<div>
<div class="events-description">
<div class="events-description">
...
@@ -19,7 +22,8 @@ global.cycleAnalytics.StageIssueComponent = Vue.extend({
...
@@ -19,7 +22,8 @@ global.cycleAnalytics.StageIssueComponent = Vue.extend({
<ul class="stage-event-list">
<ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item">
<li v-for="issue in items" class="stage-event-item">
<div class="item-details">
<div class="item-details">
<img class="avatar" :src="issue.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="issue.author.avatarUrl"/>
<h5 class="item-title issue-title">
<h5 class="item-title issue-title">
<a class="issue-title" :href="issue.url">
<a class="issue-title" :href="issue.url">
{{ issue.title }}
{{ issue.title }}
...
...
app/assets/javascripts/cycle_analytics/components/stage_plan_component.js
View file @
3c668fa0
/* eslint-disable no-param-reassign */
/* eslint-disable no-param-reassign */
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
userAvatarImage
from
'
../../vue_shared/components/user_avatar/user_avatar_image.vue
'
;
import
iconCommit
from
'
../svg/icon_commit.svg
'
;
import
iconCommit
from
'
../svg/icon_commit.svg
'
;
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
...
@@ -10,11 +11,12 @@ global.cycleAnalytics.StagePlanComponent = Vue.extend({
...
@@ -10,11 +11,12 @@ global.cycleAnalytics.StagePlanComponent = Vue.extend({
items
:
Array
,
items
:
Array
,
stage
:
Object
,
stage
:
Object
,
},
},
components
:
{
userAvatarImage
,
},
data
()
{
data
()
{
return
{
iconCommit
};
return
{
iconCommit
};
},
},
template
:
`
template
:
`
<div>
<div>
<div class="events-description">
<div class="events-description">
...
@@ -24,7 +26,8 @@ global.cycleAnalytics.StagePlanComponent = Vue.extend({
...
@@ -24,7 +26,8 @@ global.cycleAnalytics.StagePlanComponent = Vue.extend({
<ul class="stage-event-list">
<ul class="stage-event-list">
<li v-for="commit in items" class="stage-event-item">
<li v-for="commit in items" class="stage-event-item">
<div class="item-details item-conmmit-component">
<div class="item-details item-conmmit-component">
<img class="avatar" :src="commit.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="commit.author.avatarUrl"/>
<h5 class="item-title commit-title">
<h5 class="item-title commit-title">
<a :href="commit.commitUrl">
<a :href="commit.commitUrl">
{{ commit.title }}
{{ commit.title }}
...
...
app/assets/javascripts/cycle_analytics/components/stage_production_component.js
View file @
3c668fa0
/* eslint-disable no-param-reassign */
/* eslint-disable no-param-reassign */
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
userAvatarImage
from
'
../../vue_shared/components/user_avatar/user_avatar_image.vue
'
;
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
global
.
cycleAnalytics
=
global
.
cycleAnalytics
||
{};
global
.
cycleAnalytics
=
global
.
cycleAnalytics
||
{};
...
@@ -10,6 +10,9 @@ global.cycleAnalytics.StageProductionComponent = Vue.extend({
...
@@ -10,6 +10,9 @@ global.cycleAnalytics.StageProductionComponent = Vue.extend({
items
:
Array
,
items
:
Array
,
stage
:
Object
,
stage
:
Object
,
},
},
components
:
{
userAvatarImage
,
},
template
:
`
template
:
`
<div>
<div>
<div class="events-description">
<div class="events-description">
...
@@ -19,7 +22,8 @@ global.cycleAnalytics.StageProductionComponent = Vue.extend({
...
@@ -19,7 +22,8 @@ global.cycleAnalytics.StageProductionComponent = Vue.extend({
<ul class="stage-event-list">
<ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item">
<li v-for="issue in items" class="stage-event-item">
<div class="item-details">
<div class="item-details">
<img class="avatar" :src="issue.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="issue.author.avatarUrl"/>
<h5 class="item-title issue-title">
<h5 class="item-title issue-title">
<a class="issue-title" :href="issue.url">
<a class="issue-title" :href="issue.url">
{{ issue.title }}
{{ issue.title }}
...
...
app/assets/javascripts/cycle_analytics/components/stage_review_component.js
View file @
3c668fa0
/* eslint-disable no-param-reassign */
/* eslint-disable no-param-reassign */
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
userAvatarImage
from
'
../../vue_shared/components/user_avatar/user_avatar_image.vue
'
;
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
global
.
cycleAnalytics
=
global
.
cycleAnalytics
||
{};
global
.
cycleAnalytics
=
global
.
cycleAnalytics
||
{};
...
@@ -10,6 +10,9 @@ global.cycleAnalytics.StageReviewComponent = Vue.extend({
...
@@ -10,6 +10,9 @@ global.cycleAnalytics.StageReviewComponent = Vue.extend({
items
:
Array
,
items
:
Array
,
stage
:
Object
,
stage
:
Object
,
},
},
components
:
{
userAvatarImage
,
},
template
:
`
template
:
`
<div>
<div>
<div class="events-description">
<div class="events-description">
...
@@ -19,7 +22,8 @@ global.cycleAnalytics.StageReviewComponent = Vue.extend({
...
@@ -19,7 +22,8 @@ global.cycleAnalytics.StageReviewComponent = Vue.extend({
<ul class="stage-event-list">
<ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item">
<li v-for="mergeRequest in items" class="stage-event-item">
<div class="item-details">
<div class="item-details">
<img class="avatar" :src="mergeRequest.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="mergeRequest.author.avatarUrl"/>
<h5 class="item-title merge-merquest-title">
<h5 class="item-title merge-merquest-title">
<a :href="mergeRequest.url">
<a :href="mergeRequest.url">
{{ mergeRequest.title }}
{{ mergeRequest.title }}
...
...
app/assets/javascripts/cycle_analytics/components/stage_staging_component.js
View file @
3c668fa0
/* eslint-disable no-param-reassign */
/* eslint-disable no-param-reassign */
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
userAvatarImage
from
'
../../vue_shared/components/user_avatar/user_avatar_image.vue
'
;
import
iconBranch
from
'
../svg/icon_branch.svg
'
;
import
iconBranch
from
'
../svg/icon_branch.svg
'
;
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
const
global
=
window
.
gl
||
(
window
.
gl
=
{});
...
@@ -13,6 +14,9 @@ global.cycleAnalytics.StageStagingComponent = Vue.extend({
...
@@ -13,6 +14,9 @@ global.cycleAnalytics.StageStagingComponent = Vue.extend({
data
()
{
data
()
{
return
{
iconBranch
};
return
{
iconBranch
};
},
},
components
:
{
userAvatarImage
,
},
template
:
`
template
:
`
<div>
<div>
<div class="events-description">
<div class="events-description">
...
@@ -22,7 +26,8 @@ global.cycleAnalytics.StageStagingComponent = Vue.extend({
...
@@ -22,7 +26,8 @@ global.cycleAnalytics.StageStagingComponent = Vue.extend({
<ul class="stage-event-list">
<ul class="stage-event-list">
<li v-for="build in items" class="stage-event-item item-build-component">
<li v-for="build in items" class="stage-event-item item-build-component">
<div class="item-details">
<div class="item-details">
<img class="avatar" :src="build.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="build.author.avatarUrl"/>
<h5 class="item-title">
<h5 class="item-title">
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i>
<i class="fa fa-code-fork"></i>
...
...
app/assets/javascripts/diff_notes/components/diff_note_avatars.js
View file @
3c668fa0
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
collapseIcon
from
'
../icons/collapse_icon.svg
'
;
import
collapseIcon
from
'
../icons/collapse_icon.svg
'
;
import
userAvatarImage
from
'
../../vue_shared/components/user_avatar/user_avatar_image.vue
'
;
const
DiffNoteAvatars
=
Vue
.
extend
({
const
DiffNoteAvatars
=
Vue
.
extend
({
props
:
[
'
discussionId
'
],
props
:
[
'
discussionId
'
],
...
@@ -15,22 +16,24 @@ const DiffNoteAvatars = Vue.extend({
...
@@ -15,22 +16,24 @@ const DiffNoteAvatars = Vue.extend({
collapseIcon
,
collapseIcon
,
};
};
},
},
components
:
{
userAvatarImage
,
},
template
:
`
template
:
`
<div class="diff-comment-avatar-holders"
<div class="diff-comment-avatar-holders"
v-show="notesCount !== 0">
v-show="notesCount !== 0">
<div v-if="!isVisible">
<div v-if="!isVisible">
<img v-for="note in notesSubset"
<!-- FIXME: Pass an alt attribute here for accessibility -->
class="avatar diff-comment-avatar has-tooltip js-diff-comment-avatar"
<user-avatar-image
width="19"
v-for="note in notesSubset"
height="19"
class="diff-comment-avatar js-diff-comment-avatar"
role="button"
@click.native="clickedAvatar($event)"
data-container="body"
:img-src="note.authorAvatar"
data-placement="top"
:tooltip-text="getTooltipText(note)"
data-html="true"
:data-line-type="lineType"
:data-line-type="lineType"
:
title="note.authorName + ': ' + note.noteTruncated
"
:
size="19
"
:src="note.authorAvatar
"
data-html="true
"
@click="clickedAvatar($event)"
/>
/>
<span v-if="notesCount > shownAvatars"
<span v-if="notesCount > shownAvatars"
class="diff-comments-more-count has-tooltip js-diff-comment-avatar"
class="diff-comments-more-count has-tooltip js-diff-comment-avatar"
data-container="body"
data-container="body"
...
@@ -150,6 +153,9 @@ const DiffNoteAvatars = Vue.extend({
...
@@ -150,6 +153,9 @@ const DiffNoteAvatars = Vue.extend({
setDiscussionVisible
()
{
setDiscussionVisible
()
{
this
.
isVisible
=
$
(
`.diffs .notes[data-discussion-id="
${
this
.
discussion
.
id
}
"]`
).
is
(
'
:visible
'
);
this
.
isVisible
=
$
(
`.diffs .notes[data-discussion-id="
${
this
.
discussion
.
id
}
"]`
).
is
(
'
:visible
'
);
},
},
getTooltipText
(
note
)
{
return
`
${
note
.
authorName
}
:
${
note
.
noteTruncated
}
`
;
},
},
},
});
});
...
...
app/assets/javascripts/environments/components/environment_item.vue
View file @
3c668fa0
<
script
>
<
script
>
import
Timeago
from
'
timeago.js
'
;
import
Timeago
from
'
timeago.js
'
;
import
_
from
'
underscore
'
;
import
_
from
'
underscore
'
;
import
userAvatarLink
from
'
../../vue_shared/components/user_avatar/user_avatar_link.vue
'
;
import
'
../../lib/utils/text_utility
'
;
import
'
../../lib/utils/text_utility
'
;
import
ActionsComponent
from
'
./environment_actions.vue
'
;
import
ActionsComponent
from
'
./environment_actions.vue
'
;
import
ExternalUrlComponent
from
'
./environment_external_url.vue
'
;
import
ExternalUrlComponent
from
'
./environment_external_url.vue
'
;
...
@@ -20,6 +21,7 @@ const timeagoInstance = new Timeago();
...
@@ -20,6 +21,7 @@ const timeagoInstance = new Timeago();
export
default
{
export
default
{
components
:
{
components
:
{
userAvatarLink
,
'
commit-component
'
:
CommitComponent
,
'
commit-component
'
:
CommitComponent
,
'
actions-component
'
:
ActionsComponent
,
'
actions-component
'
:
ActionsComponent
,
'
external-url-component
'
:
ExternalUrlComponent
,
'
external-url-component
'
:
ExternalUrlComponent
,
...
@@ -468,15 +470,13 @@ export default {
...
@@ -468,15 +470,13 @@ export default {
<span
v-if=
"!model.isFolder && deploymentHasUser"
>
<span
v-if=
"!model.isFolder && deploymentHasUser"
>
by
by
<a
<user-avatar-link
:href=
"deploymentUser.web_url"
class=
"js-deploy-user-container"
class=
"js-deploy-user-container"
>
:link-href=
"deploymentUser.web_url"
<img
:img-src=
"deploymentUser.avatar_url"
class=
"avatar has-tooltip s20"
:img-alt=
"userImageAltDescription"
:src=
"deploymentUser.avatar_url"
:tooltip-text=
"deploymentUser.username"
:alt=
"userImageAltDescription"
/>
:title=
"deploymentUser.username"
/>
</a>
</span>
</span>
</td>
</td>
...
...
app/assets/javascripts/pipelines/components/pipeline_url.js
View file @
3c668fa0
import
userAvatarLink
from
'
../../vue_shared/components/user_avatar/user_avatar_link.vue
'
;
export
default
{
export
default
{
props
:
[
props
:
[
'
pipeline
'
,
'
pipeline
'
,
...
@@ -7,6 +9,9 @@ export default {
...
@@ -7,6 +9,9 @@ export default {
return
!!
this
.
pipeline
.
user
;
return
!!
this
.
pipeline
.
user
;
},
},
},
},
components
:
{
userAvatarLink
,
},
template
:
`
template
:
`
<td>
<td>
<a
<a
...
@@ -15,18 +20,13 @@ export default {
...
@@ -15,18 +20,13 @@ export default {
<span class="pipeline-id">#{{pipeline.id}}</span>
<span class="pipeline-id">#{{pipeline.id}}</span>
</a>
</a>
<span>by</span>
<span>by</span>
<a
<user-avatar-link
class="js-pipeline-url-user"
v-if="user"
v-if="user"
:href="pipeline.user.web_url">
class="js-pipeline-url-user"
<img
:link-href="pipeline.user.web_url"
v-if="user"
:img-src="pipeline.user.avatar_url"
class="avatar has-tooltip s20 "
:tooltip-text="pipeline.user.name"
:title="pipeline.user.name"
/>
data-container="body"
:src="pipeline.user.avatar_url"
>
</a>
<span
<span
v-if="!user"
v-if="!user"
class="js-pipeline-url-api api">
class="js-pipeline-url-api api">
...
...
app/assets/javascripts/vue_shared/components/commit.js
View file @
3c668fa0
import
commitIconSvg
from
'
icons/_icon_commit.svg
'
;
import
commitIconSvg
from
'
icons/_icon_commit.svg
'
;
import
userAvatarLink
from
'
./user_avatar/user_avatar_link.vue
'
;
export
default
{
export
default
{
props
:
{
props
:
{
...
@@ -110,6 +111,9 @@ export default {
...
@@ -110,6 +111,9 @@ export default {
return
{
commitIconSvg
};
return
{
commitIconSvg
};
},
},
components
:
{
userAvatarLink
,
},
template
:
`
template
:
`
<div class="branch-commit">
<div class="branch-commit">
...
@@ -133,16 +137,14 @@ export default {
...
@@ -133,16 +137,14 @@ export default {
<p class="commit-title">
<p class="commit-title">
<span v-if="title">
<span v-if="title">
<a v-if="hasAuthor"
<user-avatar-link
v-if="hasAuthor"
class="avatar-image-container"
class="avatar-image-container"
:href="author.web_url">
:link-href="author.web_url"
<img
:img-src="author.avatar_url"
class="avatar has-tooltip s20"
:img-alt="userImageAltDescription"
:src="author.avatar_url"
:tooltip-text="author.username"
:alt="userImageAltDescription"
/>
:title="author.username" />
</a>
<a class="commit-row-message"
<a class="commit-row-message"
:href="commitUrl">
:href="commitUrl">
{{title}}
{{title}}
...
...
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
0 → 100644
View file @
3c668fa0
<
script
>
/* This is a re-usable vue component for rendering a user avatar that
does not need to link to the user's profile. The image and an optional
tooltip can be configured by props passed to this component.
Sample configuration:
<user-avatar-image
:img-src="userAvatarSrc"
:img-alt="tooltipText"
:tooltip-text="tooltipText"
tooltip-placement="top"
/>
*/
import
defaultAvatarUrl
from
'
images/no_avatar.png
'
;
import
UserAvatarSizeMixin
from
'
./user_avatar_size_mixin
'
;
import
TooltipMixin
from
'
../../mixins/tooltip
'
;
export
default
{
name
:
'
UserAvatarImage
'
,
mixins
:
[
UserAvatarSizeMixin
,
TooltipMixin
],
props
:
{
imgSrc
:
{
type
:
String
,
required
:
false
,
default
:
defaultAvatarUrl
,
},
cssClasses
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
imgAlt
:
{
type
:
String
,
required
:
false
,
default
:
'
user avatar
'
,
},
size
:
{
type
:
Number
,
required
:
false
,
default
:
20
,
},
tooltipText
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
tooltipPlacement
:
{
type
:
String
,
required
:
false
,
default
:
'
top
'
,
},
},
computed
:
{
tooltipContainer
()
{
return
this
.
tooltipText
?
'
body
'
:
null
;
},
imgCssClasses
()
{
return
`avatar
${
this
.
avatarSizeClass
}
${
this
.
cssClasses
}
`
;
},
},
};
</
script
>
<
template
>
<img
:class=
"imgCssClasses"
:src=
"imgSrc"
:style=
"avatarSizeStylesMap"
:alt=
"imgAlt"
:data-container=
"tooltipContainer"
:data-placement=
"tooltipPlacement"
:title=
"tooltipText"
ref=
"tooltip"
/>
</
template
>
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
0 → 100644
View file @
3c668fa0
<
script
>
/* This is a re-usable vue component for rendering a user avatar wrapped in
a clickable link (likely to the user's profile). The link, image, and
tooltip can be configured by props passed to this component.
Sample configuration:
<user-avatar-link
:link-href="userProfileUrl"
:img-src="userAvatarSrc"
:img-alt="tooltipText"
:img-size="20"
:tooltip-text="tooltipText"
tooltip-placement="top"
/>
*/
import
userAvatarImage
from
'
./user_avatar_image.vue
'
;
export
default
{
name
:
'
UserAvatarLink
'
,
components
:
{
userAvatarImage
,
},
props
:
{
linkHref
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
imgSrc
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
imgAlt
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
imgCssClasses
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
imgSize
:
{
type
:
Number
,
required
:
false
,
default
:
20
,
},
tooltipText
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
tooltipPlacement
:
{
type
:
String
,
required
:
false
,
default
:
'
top
'
,
},
},
};
</
script
>
<
template
>
<a
class=
"user-avatar-link"
:href=
"linkHref"
>
<user-avatar-image
:img-src=
"imgSrc"
:img-alt=
"imgAlt"
:css-classes=
"imgCssClasses"
:size=
"imgSize"
:tooltip-text=
"tooltipText"
:tooltip-placement=
"tooltipPlacement"
/>
</a>
</
template
>
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_size_mixin.js
0 → 100644
View file @
3c668fa0
export
default
{
computed
:
{
avatarSizeStylesMap
()
{
return
{
width
:
`
${
this
.
size
}
px`
,
height
:
`
${
this
.
size
}
px`
,
};
},
avatarSizeClass
()
{
return
`s
${
this
.
size
}
`
;
},
},
};
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_svg.vue
0 → 100644
View file @
3c668fa0
<
script
>
/* This is a re-usable vue component for rendering a user avatar svg (typically
for a blank state). It will receive styles comparable to the user avatar,
but no image is loaded, it isn't wrapped in a link, and tooltips aren't supported.
The svg and avatar size can be configured by props passed to this component.
Sample configuration:
<user-avatar-svg
:svg="potentialApproverSvg"
:size="20"
/>
*/
import
UserAvatarSizeMixin
from
'
./user_avatar_size_mixin
'
;
export
default
{
mixins
:
[
UserAvatarSizeMixin
],
props
:
{
svg
:
{
type
:
String
,
required
:
true
,
},
size
:
{
type
:
Number
,
required
:
false
,
default
:
20
,
},
},
};
</
script
>
<
template
>
<svg
:class=
"avatarSizeClass"
:style=
"avatarSizeStylesMap"
v-html=
"svg"
>
</svg>
</
template
>
app/assets/stylesheets/framework/icons.scss
View file @
3c668fa0
...
@@ -65,3 +65,7 @@
...
@@ -65,3 +65,7 @@
text-decoration
:
none
;
text-decoration
:
none
;
}
}
}
}
.user-avatar-link
{
text-decoration
:
none
;
}
app/assets/stylesheets/pages/environments.scss
View file @
3c668fa0
...
@@ -68,10 +68,6 @@
...
@@ -68,10 +68,6 @@
margin
:
0
;
margin
:
0
;
}
}
.avatar-image-container
{
text-decoration
:
none
;
}
.icon-play
{
.icon-play
{
height
:
13px
;
height
:
13px
;
width
:
12px
;
width
:
12px
;
...
...
config/webpack.config.js
View file @
3c668fa0
...
@@ -90,9 +90,9 @@ var config = {
...
@@ -90,9 +90,9 @@ var config = {
loader
:
'
raw-loader
'
,
loader
:
'
raw-loader
'
,
},
},
{
{
test
:
/
\.
gif
$/
,
test
:
/
\.
(
gif|png
)
$/
,
loader
:
'
url-loader
'
,
loader
:
'
url-loader
'
,
query
:
{
mimetype
:
'
image/gif
'
},
options
:
{
limit
:
2048
},
},
},
{
{
test
:
/
\.(
worker
\.
js|pdf|bmpr
)
$/
,
test
:
/
\.(
worker
\.
js|pdf|bmpr
)
$/
,
...
@@ -190,6 +190,7 @@ var config = {
...
@@ -190,6 +190,7 @@ var config = {
'
emojis
'
:
path
.
join
(
ROOT_PATH
,
'
fixtures/emojis
'
),
'
emojis
'
:
path
.
join
(
ROOT_PATH
,
'
fixtures/emojis
'
),
'
empty_states
'
:
path
.
join
(
ROOT_PATH
,
'
app/views/shared/empty_states
'
),
'
empty_states
'
:
path
.
join
(
ROOT_PATH
,
'
app/views/shared/empty_states
'
),
'
icons
'
:
path
.
join
(
ROOT_PATH
,
'
app/views/shared/icons
'
),
'
icons
'
:
path
.
join
(
ROOT_PATH
,
'
app/views/shared/icons
'
),
'
images
'
:
path
.
join
(
ROOT_PATH
,
'
app/assets/images
'
),
'
vendor
'
:
path
.
join
(
ROOT_PATH
,
'
vendor/assets/javascripts
'
),
'
vendor
'
:
path
.
join
(
ROOT_PATH
,
'
vendor/assets/javascripts
'
),
'
vue$
'
:
'
vue/dist/vue.esm.js
'
,
'
vue$
'
:
'
vue/dist/vue.esm.js
'
,
}
}
...
...
spec/features/merge_requests/diff_notes_avatars_spec.rb
View file @
3c668fa0
...
@@ -91,7 +91,7 @@ feature 'Diff note avatars', feature: true, js: true do
...
@@ -91,7 +91,7 @@ feature 'Diff note avatars', feature: true, js: true do
page
.
within
find
(
"[id='
#{
position
.
line_code
(
project
.
repository
)
}
']"
)
do
page
.
within
find
(
"[id='
#{
position
.
line_code
(
project
.
repository
)
}
']"
)
do
find
(
'.diff-notes-collapse'
).
click
find
(
'.diff-notes-collapse'
).
click
expect
(
first
(
'img.js-diff-comment-avatar'
)[
"title"
]).
to
eq
(
"
#{
note
.
author
.
name
}
:
#{
note
.
note
.
truncate
(
17
)
}
"
)
expect
(
first
(
'img.js-diff-comment-avatar'
)[
"
data-original-
title"
]).
to
eq
(
"
#{
note
.
author
.
name
}
:
#{
note
.
note
.
truncate
(
17
)
}
"
)
end
end
end
end
...
...
spec/javascripts/boards/issue_card_spec.js
View file @
3c668fa0
...
@@ -129,7 +129,7 @@ describe('Issue card component', () => {
...
@@ -129,7 +129,7 @@ describe('Issue card component', () => {
it
(
'
sets title
'
,
()
=>
{
it
(
'
sets title
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.card-assignee
a
'
).
getAttribute
(
'
title
'
),
component
.
$el
.
querySelector
(
'
.card-assignee
img
'
).
getAttribute
(
'
data-original-
title
'
),
).
toContain
(
`Assigned to
${
user
.
name
}
`
);
).
toContain
(
`Assigned to
${
user
.
name
}
`
);
});
});
...
...
spec/javascripts/pipelines/pipeline_url_spec.js
View file @
3c668fa0
...
@@ -60,7 +60,7 @@ describe('Pipeline Url Component', () => {
...
@@ -60,7 +60,7 @@ describe('Pipeline Url Component', () => {
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-user
'
).
getAttribute
(
'
href
'
),
component
.
$el
.
querySelector
(
'
.js-pipeline-url-user
'
).
getAttribute
(
'
href
'
),
).
toEqual
(
mockData
.
pipeline
.
user
.
web_url
);
).
toEqual
(
mockData
.
pipeline
.
user
.
web_url
);
expect
(
image
.
getAttribute
(
'
title
'
)).
toEqual
(
mockData
.
pipeline
.
user
.
name
);
expect
(
image
.
getAttribute
(
'
data-original-
title
'
)).
toEqual
(
mockData
.
pipeline
.
user
.
name
);
expect
(
image
.
getAttribute
(
'
src
'
)).
toEqual
(
mockData
.
pipeline
.
user
.
avatar_url
);
expect
(
image
.
getAttribute
(
'
src
'
)).
toEqual
(
mockData
.
pipeline
.
user
.
avatar_url
);
});
});
...
...
spec/javascripts/vue_shared/components/commit_spec.js
View file @
3c668fa0
...
@@ -86,7 +86,7 @@ describe('Commit component', () => {
...
@@ -86,7 +86,7 @@ describe('Commit component', () => {
it
(
'
Should render the author avatar with title and alt attributes
'
,
()
=>
{
it
(
'
Should render the author avatar with title and alt attributes
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.commit-title .avatar-image-container img
'
).
getAttribute
(
'
title
'
),
component
.
$el
.
querySelector
(
'
.commit-title .avatar-image-container img
'
).
getAttribute
(
'
data-original-
title
'
),
).
toContain
(
props
.
author
.
username
);
).
toContain
(
props
.
author
.
username
);
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.commit-title .avatar-image-container img
'
).
getAttribute
(
'
alt
'
),
component
.
$el
.
querySelector
(
'
.commit-title .avatar-image-container img
'
).
getAttribute
(
'
alt
'
),
...
...
spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
View file @
3c668fa0
...
@@ -79,7 +79,7 @@ describe('Pipelines Table Row', () => {
...
@@ -79,7 +79,7 @@ describe('Pipelines Table Row', () => {
).
toEqual
(
pipeline
.
user
.
web_url
);
).
toEqual
(
pipeline
.
user
.
web_url
);
expect
(
expect
(
component
.
$el
.
querySelector
(
'
td:nth-child(2) img
'
).
getAttribute
(
'
title
'
),
component
.
$el
.
querySelector
(
'
td:nth-child(2) img
'
).
getAttribute
(
'
data-original-
title
'
),
).
toEqual
(
pipeline
.
user
.
name
);
).
toEqual
(
pipeline
.
user
.
name
);
});
});
});
});
...
@@ -102,7 +102,7 @@ describe('Pipelines Table Row', () => {
...
@@ -102,7 +102,7 @@ describe('Pipelines Table Row', () => {
}
}
const
commitAuthorLink
=
commitAuthorElement
.
getAttribute
(
'
href
'
);
const
commitAuthorLink
=
commitAuthorElement
.
getAttribute
(
'
href
'
);
const
commitAuthorName
=
commitAuthorElement
.
querySelector
(
'
img.avatar
'
).
getAttribute
(
'
title
'
);
const
commitAuthorName
=
commitAuthorElement
.
querySelector
(
'
img.avatar
'
).
getAttribute
(
'
data-original-
title
'
);
return
{
commitAuthorElement
,
commitAuthorLink
,
commitAuthorName
};
return
{
commitAuthorElement
,
commitAuthorLink
,
commitAuthorName
};
};
};
...
...
spec/javascripts/vue_shared/components/user_avatar_image_spec.js
0 → 100644
View file @
3c668fa0
import
Vue
from
'
vue
'
;
import
UserAvatarImage
from
'
~/vue_shared/components/user_avatar/user_avatar_image.vue
'
;
const
UserAvatarImageComponent
=
Vue
.
extend
(
UserAvatarImage
);
describe
(
'
User Avatar Image Component
'
,
function
()
{
describe
(
'
Initialization
'
,
function
()
{
beforeEach
(
function
()
{
this
.
propsData
=
{
size
:
99
,
imgSrc
:
'
myavatarurl.com
'
,
imgAlt
:
'
mydisplayname
'
,
cssClasses
:
'
myextraavatarclass
'
,
tooltipText
:
'
tooltip text
'
,
tooltipPlacement
:
'
bottom
'
,
};
this
.
userAvatarImage
=
new
UserAvatarImageComponent
({
propsData
:
this
.
propsData
,
}).
$mount
();
this
.
imageElement
=
this
.
userAvatarImage
.
$el
.
outerHTML
;
});
it
(
'
should return a defined Vue component
'
,
function
()
{
expect
(
this
.
userAvatarImage
).
toBeDefined
();
});
it
(
'
should have <img> as a child element
'
,
function
()
{
const
componentImgTag
=
this
.
userAvatarImage
.
$el
.
outerHTML
;
expect
(
componentImgTag
).
toContain
(
'
<img
'
);
});
it
(
'
should return neccessary props as defined
'
,
function
()
{
_
.
each
(
this
.
propsData
,
(
val
,
key
)
=>
{
expect
(
this
.
userAvatarImage
[
key
]).
toBeDefined
();
});
});
it
(
'
should properly compute tooltipContainer
'
,
function
()
{
expect
(
this
.
userAvatarImage
.
tooltipContainer
).
toBe
(
'
body
'
);
});
it
(
'
should properly render tooltipContainer
'
,
function
()
{
expect
(
this
.
imageElement
).
toContain
(
'
data-container="body"
'
);
});
it
(
'
should properly compute avatarSizeClass
'
,
function
()
{
expect
(
this
.
userAvatarImage
.
avatarSizeClass
).
toBe
(
'
s99
'
);
});
it
(
'
should properly compute imgCssClasses
'
,
function
()
{
expect
(
this
.
userAvatarImage
.
imgCssClasses
).
toBe
(
'
avatar s99 myextraavatarclass
'
);
});
it
(
'
should properly render imgCssClasses
'
,
function
()
{
expect
(
this
.
imageElement
).
toContain
(
'
avatar s99 myextraavatarclass
'
);
});
});
});
spec/javascripts/vue_shared/components/user_avatar_link_spec.js
0 → 100644
View file @
3c668fa0
import
Vue
from
'
vue
'
;
import
UserAvatarLink
from
'
~/vue_shared/components/user_avatar/user_avatar_link.vue
'
;
describe
(
'
User Avatar Link Component
'
,
function
()
{
beforeEach
(
function
()
{
this
.
propsData
=
{
linkHref
:
'
myavatarurl.com
'
,
imgSize
:
99
,
imgSrc
:
'
myavatarurl.com
'
,
imgAlt
:
'
mydisplayname
'
,
imgCssClasses
:
'
myextraavatarclass
'
,
tooltipText
:
'
tooltip text
'
,
tooltipPlacement
:
'
bottom
'
,
};
const
UserAvatarLinkComponent
=
Vue
.
extend
(
UserAvatarLink
);
this
.
userAvatarLink
=
new
UserAvatarLinkComponent
({
propsData
:
this
.
propsData
,
}).
$mount
();
this
.
userAvatarImage
=
this
.
userAvatarLink
.
$children
[
0
];
});
it
(
'
should return a defined Vue component
'
,
function
()
{
expect
(
this
.
userAvatarLink
).
toBeDefined
();
});
it
(
'
should have user-avatar-image registered as child component
'
,
function
()
{
expect
(
this
.
userAvatarLink
.
$options
.
components
.
userAvatarImage
).
toBeDefined
();
});
it
(
'
user-avatar-link should have user-avatar-image as child component
'
,
function
()
{
expect
(
this
.
userAvatarImage
).
toBeDefined
();
});
it
(
'
should render <a> as a child element
'
,
function
()
{
const
componentLinkTag
=
this
.
userAvatarLink
.
$el
.
outerHTML
;
expect
(
componentLinkTag
).
toContain
(
'
<a
'
);
});
it
(
'
should have <img> as a child element
'
,
function
()
{
const
componentImgTag
=
this
.
userAvatarLink
.
$el
.
outerHTML
;
expect
(
componentImgTag
).
toContain
(
'
<img
'
);
});
it
(
'
should return neccessary props as defined
'
,
function
()
{
_
.
each
(
this
.
propsData
,
(
val
,
key
)
=>
{
expect
(
this
.
userAvatarLink
[
key
]).
toBeDefined
();
});
});
it
(
'
should include props in the rendered output
'
,
function
()
{
_
.
each
(
this
.
propsData
,
(
val
)
=>
{
expect
(
this
.
userAvatarLink
.
$el
.
outerHTML
).
toContain
(
val
);
});
});
});
spec/javascripts/vue_shared/components/user_avatar_size_mixin_spec.js
0 → 100644
View file @
3c668fa0
import
Vue
from
'
vue
'
;
import
UserAvatarSizeMixin
from
'
~/vue_shared/components/user_avatar/user_avatar_size_mixin
'
;
describe
(
'
User Avatar Size Mixin
'
,
()
=>
{
beforeEach
(()
=>
{
this
.
vueInstance
=
new
Vue
({
data
:
{
size
:
99
,
},
mixins
:
[
UserAvatarSizeMixin
],
});
});
describe
(
'
#avatarSizeClass
'
,
()
=>
{
it
(
'
should be a defined computed value
'
,
()
=>
{
expect
(
this
.
vueInstance
.
avatarSizeClass
).
toBeDefined
();
});
it
(
'
should correctly transform size into the class name
'
,
()
=>
{
expect
(
this
.
vueInstance
.
avatarSizeClass
).
toBe
(
'
s99
'
);
});
});
describe
(
'
#avatarSizeStylesMap
'
,
()
=>
{
it
(
'
should be a defined computed value
'
,
()
=>
{
expect
(
this
.
vueInstance
.
avatarSizeStylesMap
).
toBeDefined
();
});
it
(
'
should return a correctly formatted styles width
'
,
()
=>
{
expect
(
this
.
vueInstance
.
avatarSizeStylesMap
.
width
).
toBe
(
'
99px
'
);
});
it
(
'
should return a correctly formatted styles height
'
,
()
=>
{
expect
(
this
.
vueInstance
.
avatarSizeStylesMap
.
height
).
toBe
(
'
99px
'
);
});
});
});
spec/javascripts/vue_shared/components/user_avatar_svg_spec.js
0 → 100644
View file @
3c668fa0
import
Vue
from
'
vue
'
;
import
UserAvatarSvg
from
'
~/vue_shared/components/user_avatar/user_avatar_svg.vue
'
;
import
avatarSvg
from
'
icons/_icon_random.svg
'
;
const
UserAvatarSvgComponent
=
Vue
.
extend
(
UserAvatarSvg
);
describe
(
'
User Avatar Svg Component
'
,
function
()
{
describe
(
'
Initialization
'
,
function
()
{
beforeEach
(
function
()
{
this
.
propsData
=
{
size
:
99
,
svg
:
avatarSvg
,
};
this
.
userAvatarSvg
=
new
UserAvatarSvgComponent
({
propsData
:
this
.
propsData
,
}).
$mount
();
});
it
(
'
should return a defined Vue component
'
,
function
()
{
expect
(
this
.
userAvatarSvg
).
toBeDefined
();
});
it
(
'
should have <svg> as a child element
'
,
function
()
{
expect
(
this
.
userAvatarSvg
.
$el
.
tagName
).
toEqual
(
'
svg
'
);
expect
(
this
.
userAvatarSvg
.
$el
.
innerHTML
).
toContain
(
'
<path
'
);
});
it
(
'
should return neccessary props as defined
'
,
function
()
{
_
.
each
(
this
.
propsData
,
(
val
,
key
)
=>
{
expect
(
this
.
userAvatarSvg
[
key
]).
toBeDefined
();
});
});
});
});
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