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
cd5ddc4f
Commit
cd5ddc4f
authored
Nov 08, 2018
by
Fatih Acet
Committed by
Mike Greiling
Nov 08, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Discussions redesign
parent
b5a79f15
Changes
24
Show whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
629 additions
and
276 deletions
+629
-276
app/assets/javascripts/diffs/components/diff_discussions.vue
app/assets/javascripts/diffs/components/diff_discussions.vue
+0
-1
app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
...avascripts/diffs/components/parallel_diff_comment_row.vue
+6
-4
app/assets/javascripts/notes/components/comment_form.vue
app/assets/javascripts/notes/components/comment_form.vue
+4
-4
app/assets/javascripts/notes/components/discussion_counter.vue
...ssets/javascripts/notes/components/discussion_counter.vue
+9
-21
app/assets/javascripts/notes/components/note_actions.vue
app/assets/javascripts/notes/components/note_actions.vue
+21
-42
app/assets/javascripts/notes/components/note_awards_list.vue
app/assets/javascripts/notes/components/note_awards_list.vue
+10
-17
app/assets/javascripts/notes/components/note_header.vue
app/assets/javascripts/notes/components/note_header.vue
+7
-5
app/assets/javascripts/notes/components/noteable_discussion.vue
...sets/javascripts/notes/components/noteable_discussion.vue
+113
-57
app/assets/javascripts/notes/components/noteable_note.vue
app/assets/javascripts/notes/components/noteable_note.vue
+2
-1
app/assets/javascripts/notes/components/toggle_replies_widget.vue
...ts/javascripts/notes/components/toggle_replies_widget.vue
+94
-0
app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue
...javascripts/vue_shared/components/notes/skeleton_note.vue
+1
-1
app/assets/javascripts/vue_shared/components/notes/system_note.vue
...s/javascripts/vue_shared/components/notes/system_note.vue
+1
-1
app/assets/stylesheets/framework/awards.scss
app/assets/stylesheets/framework/awards.scss
+1
-4
app/assets/stylesheets/framework/buttons.scss
app/assets/stylesheets/framework/buttons.scss
+19
-19
app/assets/stylesheets/framework/files.scss
app/assets/stylesheets/framework/files.scss
+0
-1
app/assets/stylesheets/framework/timeline.scss
app/assets/stylesheets/framework/timeline.scss
+2
-4
app/assets/stylesheets/pages/diff.scss
app/assets/stylesheets/pages/diff.scss
+2
-1
app/assets/stylesheets/pages/note_form.scss
app/assets/stylesheets/pages/note_form.scss
+13
-1
app/assets/stylesheets/pages/notes.scss
app/assets/stylesheets/pages/notes.scss
+208
-85
app/views/shared/notes/_notes_with_form.html.haml
app/views/shared/notes/_notes_with_form.html.haml
+2
-2
locale/gitlab.pot
locale/gitlab.pot
+11
-0
spec/javascripts/notes/components/noteable_discussion_spec.js
.../javascripts/notes/components/noteable_discussion_spec.js
+14
-2
spec/javascripts/notes/components/toggle_replies_widget_spec.js
...avascripts/notes/components/toggle_replies_widget_spec.js
+78
-0
spec/support/features/discussion_comments_shared_example.rb
spec/support/features/discussion_comments_shared_example.rb
+11
-3
No files found.
app/assets/javascripts/diffs/components/diff_discussions.vue
View file @
cd5ddc4f
...
@@ -76,7 +76,6 @@ export default {
...
@@ -76,7 +76,6 @@ export default {
<noteable-discussion
<noteable-discussion
v-show=
"isExpanded(discussion)"
v-show=
"isExpanded(discussion)"
:discussion=
"discussion"
:discussion=
"discussion"
:render-header=
"false"
:render-diff-file=
"false"
:render-diff-file=
"false"
:always-expanded=
"true"
:always-expanded=
"true"
:discussions-by-diff-order=
"true"
:discussions-by-diff-order=
"true"
...
...
app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
View file @
cd5ddc4f
...
@@ -76,8 +76,9 @@ export default {
...
@@ -76,8 +76,9 @@ export default {
:class=
"className"
:class=
"className"
class=
"notes_holder"
class=
"notes_holder"
>
>
<td
class=
"notes_line old"
></td>
<td
<td
class=
"notes_content parallel old"
>
class=
"notes_content parallel old"
colspan=
"2"
>
<div
<div
v-if=
"shouldRenderDiscussionsOnLeft"
v-if=
"shouldRenderDiscussionsOnLeft"
class=
"content"
class=
"content"
...
@@ -95,8 +96,9 @@ export default {
...
@@ -95,8 +96,9 @@ export default {
line-position=
"left"
line-position=
"left"
/>
/>
</td>
</td>
<td
class=
"notes_line new"
></td>
<td
<td
class=
"notes_content parallel new"
>
class=
"notes_content parallel new"
colspan=
"2"
>
<div
<div
v-if=
"shouldRenderDiscussionsOnRight"
v-if=
"shouldRenderDiscussionsOnRight"
class=
"content"
class=
"content"
...
...
app/assets/javascripts/notes/components/comment_form.vue
View file @
cd5ddc4f
...
@@ -321,10 +321,10 @@ Please check your network connection and try again.`;
...
@@ -321,10 +321,10 @@ Please check your network connection and try again.`;
v-else-if=
"!canCreateNote"
v-else-if=
"!canCreateNote"
:issuable-type=
"issuableTypeTitle"
:issuable-type=
"issuableTypeTitle"
/>
/>
<
ul
<
div
v-else-if=
"canCreateNote"
v-else-if=
"canCreateNote"
class=
"notes notes-form timeline"
>
class=
"notes notes-form timeline"
>
<
li
class=
"timeline-entry
"
>
<
div
class=
"timeline-entry note-form
"
>
<div
class=
"timeline-entry-inner"
>
<div
class=
"timeline-entry-inner"
>
<div
class=
"flash-container error-alert timeline-content"
></div>
<div
class=
"flash-container error-alert timeline-content"
></div>
<div
class=
"timeline-icon d-none d-sm-none d-md-block"
>
<div
class=
"timeline-icon d-none d-sm-none d-md-block"
>
...
@@ -462,7 +462,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
...
@@ -462,7 +462,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
</form>
</form>
</div>
</div>
</div>
</div>
</
li
>
</
div
>
</
ul
>
</
div
>
</div>
</div>
</
template
>
</
template
>
app/assets/javascripts/notes/components/discussion_counter.vue
View file @
cd5ddc4f
<
script
>
<
script
>
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
resolveSvg
from
'
icons/_icon_resolve_discussion.svg
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
resolvedSvg
from
'
icons/_icon_status_success_solid.svg
'
;
import
mrIssueSvg
from
'
icons/_icon_mr_issue.svg
'
;
import
nextDiscussionSvg
from
'
icons/_next_discussion.svg
'
;
import
{
pluralize
}
from
'
../../lib/utils/text_utility
'
;
import
{
pluralize
}
from
'
../../lib/utils/text_utility
'
;
import
discussionNavigation
from
'
../mixins/discussion_navigation
'
;
import
discussionNavigation
from
'
../mixins/discussion_navigation
'
;
import
tooltip
from
'
../../vue_shared/directives/tooltip
'
;
import
tooltip
from
'
../../vue_shared/directives/tooltip
'
;
...
@@ -12,6 +9,9 @@ export default {
...
@@ -12,6 +9,9 @@ export default {
directives
:
{
directives
:
{
tooltip
,
tooltip
,
},
},
components
:
{
Icon
,
},
mixins
:
[
discussionNavigation
],
mixins
:
[
discussionNavigation
],
computed
:
{
computed
:
{
...
mapGetters
([
...
mapGetters
([
...
@@ -37,12 +37,6 @@ export default {
...
@@ -37,12 +37,6 @@ export default {
return
this
.
getNoteableData
.
create_issue_to_resolve_discussions_path
;
return
this
.
getNoteableData
.
create_issue_to_resolve_discussions_path
;
},
},
},
},
created
()
{
this
.
resolveSvg
=
resolveSvg
;
this
.
resolvedSvg
=
resolvedSvg
;
this
.
mrIssueSvg
=
mrIssueSvg
;
this
.
nextDiscussionSvg
=
nextDiscussionSvg
;
},
methods
:
{
methods
:
{
...
mapActions
([
'
expandDiscussion
'
]),
...
mapActions
([
'
expandDiscussion
'
]),
jumpToFirstUnresolvedDiscussion
()
{
jumpToFirstUnresolvedDiscussion
()
{
...
@@ -66,15 +60,9 @@ export default {
...
@@ -66,15 +60,9 @@ export default {
<span
<span
:class=
"
{ 'is-active': allResolved }"
:class=
"
{ 'is-active': allResolved }"
class="line-resolve-btn is-disabled"
class="line-resolve-btn is-disabled"
type="button">
type="button"
<span
>
v-if=
"allResolved"
<icon
name=
"check-circle"
/>
v-html=
"resolvedSvg"
></span>
<span
v-else
v-html=
"resolveSvg"
></span>
</span>
</span>
<span
class=
"line-resolve-text"
>
<span
class=
"line-resolve-text"
>
{{
resolvedDiscussionCount
}}
/
{{
discussionCount
}}
{{
countText
}}
resolved
{{
resolvedDiscussionCount
}}
/
{{
discussionCount
}}
{{
countText
}}
resolved
...
@@ -90,7 +78,7 @@ export default {
...
@@ -90,7 +78,7 @@ export default {
:title=
"s__('Resolve all discussions in new issue')"
:title=
"s__('Resolve all discussions in new issue')"
data-container=
"body"
data-container=
"body"
class=
"new-issue-for-discussion btn btn-default discussion-create-issue-btn"
>
class=
"new-issue-for-discussion btn btn-default discussion-create-issue-btn"
>
<
span
v-html=
"mrIssueSvg"
></span
>
<
icon
name=
"issue-new"
/
>
</a>
</a>
</div>
</div>
<div
<div
...
@@ -103,7 +91,7 @@ export default {
...
@@ -103,7 +91,7 @@ export default {
data-container=
"body"
data-container=
"body"
class=
"btn btn-default discussion-next-btn"
class=
"btn btn-default discussion-next-btn"
@
click=
"jumpToFirstUnresolvedDiscussion"
>
@
click=
"jumpToFirstUnresolvedDiscussion"
>
<
span
v-html=
"nextDiscussionSvg"
></span
>
<
icon
name=
"comment-next"
/
>
</button>
</button>
</div>
</div>
</div>
</div>
...
...
app/assets/javascripts/notes/components/note_actions.vue
View file @
cd5ddc4f
<
script
>
<
script
>
import
{
mapGetters
}
from
'
vuex
'
;
import
{
mapGetters
}
from
'
vuex
'
;
import
emojiSmiling
from
'
icons/_emoji_slightly_smiling_face.svg
'
;
import
emojiSmile
from
'
icons/_emoji_smile.svg
'
;
import
emojiSmiley
from
'
icons/_emoji_smiley.svg
'
;
import
editSvg
from
'
icons/_icon_pencil.svg
'
;
import
resolveDiscussionSvg
from
'
icons/_icon_resolve_discussion.svg
'
;
import
resolvedDiscussionSvg
from
'
icons/_icon_status_success_solid.svg
'
;
import
ellipsisSvg
from
'
icons/_ellipsis_v.svg
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
tooltip
from
'
~/vue_shared/directives/tooltip
'
;
import
tooltip
from
'
~/vue_shared/directives/tooltip
'
;
import
{
GlLoadingIcon
}
from
'
@gitlab-org/gitlab-ui
'
;
import
{
GlLoadingIcon
}
from
'
@gitlab-org/gitlab-ui
'
;
...
@@ -110,15 +103,6 @@ export default {
...
@@ -110,15 +103,6 @@ export default {
return
title
;
return
title
;
},
},
},
},
created
()
{
this
.
emojiSmiling
=
emojiSmiling
;
this
.
emojiSmile
=
emojiSmile
;
this
.
emojiSmiley
=
emojiSmiley
;
this
.
editSvg
=
editSvg
;
this
.
ellipsisSvg
=
ellipsisSvg
;
this
.
resolveDiscussionSvg
=
resolveDiscussionSvg
;
this
.
resolvedDiscussionSvg
=
resolvedDiscussionSvg
;
},
methods
:
{
methods
:
{
onEdit
()
{
onEdit
()
{
this
.
$emit
(
'
handleEdit
'
);
this
.
$emit
(
'
handleEdit
'
);
...
@@ -152,12 +136,7 @@ export default {
...
@@ -152,12 +136,7 @@ export default {
class="line-resolve-btn note-action-button"
class="line-resolve-btn note-action-button"
@click="onResolve">
@click="onResolve">
<template
v-if=
"!isResolving"
>
<template
v-if=
"!isResolving"
>
<div
<icon
name=
"check-circle"
/>
v-if=
"isResolved"
v-html=
"resolvedDiscussionSvg"
></div>
<div
v-else
v-html=
"resolveDiscussionSvg"
></div>
</
template
>
</
template
>
<gl-loading-icon
<gl-loading-icon
v-else
v-else
...
@@ -179,18 +158,18 @@ export default {
...
@@ -179,18 +158,18 @@ export default {
title=
"Add reaction"
title=
"Add reaction"
>
>
<gl-loading-icon
inline
/>
<gl-loading-icon
inline
/>
<
spa
n
<
ico
n
c
las
s=
"link-highlight award-control-icon-neutral"
c
ss-classe
s=
"link-highlight award-control-icon-neutral"
v-html=
"emojiSmiling"
>
name=
"emoji_slightly_smiling_face"
</span
>
/
>
<
spa
n
<
ico
n
c
las
s=
"link-highlight award-control-icon-positive"
c
ss-classe
s=
"link-highlight award-control-icon-positive"
v-html=
"emojiSmiley"
>
name=
"emoji_smiley"
</span
>
/
>
<
spa
n
<
ico
n
c
las
s=
"link-highlight award-control-icon-super-positive"
c
ss-classe
s=
"link-highlight award-control-icon-super-positive"
v-html=
"emojiSmile"
>
name=
"emoji_smiley"
</span
>
/
>
</a>
</a>
</div>
</div>
<div
<div
...
@@ -204,10 +183,10 @@ export default {
...
@@ -204,10 +183,10 @@ export default {
data-container=
"body"
data-container=
"body"
data-placement=
"bottom"
data-placement=
"bottom"
@
click=
"onEdit"
>
@
click=
"onEdit"
>
<
spa
n
<
ico
n
class=
"link-highlight
"
name=
"pencil
"
v-html=
"editSvg"
>
css-classes=
"link-highlight"
</span
>
/
>
</button>
</button>
</div>
</div>
<div
<div
...
@@ -240,10 +219,10 @@ export default {
...
@@ -240,10 +219,10 @@ export default {
data-toggle=
"dropdown"
data-toggle=
"dropdown"
data-container=
"body"
data-container=
"body"
data-placement=
"bottom"
>
data-placement=
"bottom"
>
<
spa
n
<
ico
n
c
las
s=
"icon"
c
ss-classe
s=
"icon"
v-html=
"ellipsisSvg"
>
name=
"ellipsis_v"
</span
>
/
>
</button>
</button>
<ul
class=
"dropdown-menu more-actions-dropdown dropdown-open-left"
>
<ul
class=
"dropdown-menu more-actions-dropdown dropdown-open-left"
>
<li
v-if=
"canReportAsAbuse"
>
<li
v-if=
"canReportAsAbuse"
>
...
...
app/assets/javascripts/notes/components/note_awards_list.vue
View file @
cd5ddc4f
<
script
>
<
script
>
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
emojiSmiling
from
'
icons/_emoji_slightly_smiling_face.svg
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
emojiSmile
from
'
icons/_emoji_smile.svg
'
;
import
emojiSmiley
from
'
icons/_emoji_smiley.svg
'
;
import
Flash
from
'
../../flash
'
;
import
Flash
from
'
../../flash
'
;
import
{
glEmojiTag
}
from
'
../../emoji
'
;
import
{
glEmojiTag
}
from
'
../../emoji
'
;
import
tooltip
from
'
../../vue_shared/directives/tooltip
'
;
import
tooltip
from
'
../../vue_shared/directives/tooltip
'
;
export
default
{
export
default
{
components
:
{
Icon
,
},
directives
:
{
directives
:
{
tooltip
,
tooltip
,
},
},
...
@@ -72,11 +73,6 @@ export default {
...
@@ -72,11 +73,6 @@ export default {
return
this
.
noteAuthorId
===
this
.
getUserData
.
id
;
return
this
.
noteAuthorId
===
this
.
getUserData
.
id
;
},
},
},
},
created
()
{
this
.
emojiSmiling
=
emojiSmiling
;
this
.
emojiSmile
=
emojiSmile
;
this
.
emojiSmiley
=
emojiSmiley
;
},
methods
:
{
methods
:
{
...
mapActions
([
'
toggleAwardRequest
'
]),
...
mapActions
([
'
toggleAwardRequest
'
]),
getAwardHTML
(
name
)
{
getAwardHTML
(
name
)
{
...
@@ -196,17 +192,14 @@ export default {
...
@@ -196,17 +192,14 @@ export default {
data-boundary="viewport"
data-boundary="viewport"
data-placement="bottom"
data-placement="bottom"
type="button">
type="button">
<span
<span
class=
"award-control-icon award-control-icon-neutral"
>
class=
"award-control-icon award-control-icon-neutral"
<icon
name=
"emoji_slightly_smiling_face"
/>
v-html=
"emojiSmiling"
>
</span>
</span>
<span
<span
class=
"award-control-icon award-control-icon-positive"
>
class=
"award-control-icon award-control-icon-positive"
<icon
name=
"emoji_smiley"
/>
v-html=
"emojiSmiley"
>
</span>
</span>
<span
<span
class=
"award-control-icon award-control-icon-super-positive"
>
class=
"award-control-icon award-control-icon-super-positive"
<icon
name=
"emoji_smiley"
/>
v-html=
"emojiSmile"
>
</span>
</span>
<i
<i
aria-hidden=
"true"
aria-hidden=
"true"
...
...
app/assets/javascripts/notes/components/note_header.vue
View file @
cd5ddc4f
...
@@ -45,6 +45,9 @@ export default {
...
@@ -45,6 +45,9 @@ export default {
noteTimestampLink
()
{
noteTimestampLink
()
{
return
`#note_
${
this
.
noteId
}
`
;
return
`#note_
${
this
.
noteId
}
`
;
},
},
hasAuthor
()
{
return
this
.
author
&&
Object
.
keys
(
this
.
author
).
length
;
},
},
},
methods
:
{
methods
:
{
...
mapActions
([
'
setTargetNoteHash
'
]),
...
mapActions
([
'
setTargetNoteHash
'
]),
...
@@ -76,7 +79,7 @@ export default {
...
@@ -76,7 +79,7 @@ export default {
</button>
</button>
</div>
</div>
<a
<a
v-if=
"
Object.keys(author).length
"
v-if=
"
hasAuthor
"
:href=
"author.path"
:href=
"author.path"
>
>
<span
class=
"note-header-author-name"
>
{{
author
.
name
}}
</span>
<span
class=
"note-header-author-name"
>
{{
author
.
name
}}
</span>
...
@@ -92,9 +95,6 @@ export default {
...
@@ -92,9 +95,6 @@ export default {
</span>
</span>
<span
class=
"note-headline-light"
>
<span
class=
"note-headline-light"
>
<span
class=
"note-headline-meta"
>
<span
class=
"note-headline-meta"
>
<template
v-if=
"actionText"
>
{{
actionText
}}
</
template
>
<span
class=
"system-note-message"
>
<span
class=
"system-note-message"
>
<slot></slot>
<slot></slot>
</span>
</span>
...
@@ -102,7 +102,9 @@ export default {
...
@@ -102,7 +102,9 @@ export default {
v-if=
"createdAt"
v-if=
"createdAt"
>
>
<span
class=
"system-note-separator"
>
<span
class=
"system-note-separator"
>
·
<template
v-if=
"actionText"
>
{{
actionText
}}
</
template
>
</span>
</span>
<a
<a
:href=
"noteTimestampLink"
:href=
"noteTimestampLink"
...
...
app/assets/javascripts/notes/components/noteable_discussion.vue
View file @
cd5ddc4f
<
script
>
<
script
>
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
resolveDiscussionsSvg
from
'
icons/_icon_mr_issue.svg
'
;
import
nextDiscussionsSvg
from
'
icons/_next_discussion.svg
'
;
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
{
truncateSha
}
from
'
~/lib/utils/text_utility
'
;
import
{
truncateSha
}
from
'
~/lib/utils/text_utility
'
;
import
systemNote
from
'
~/vue_shared/components/notes/system_note.vue
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
s__
}
from
'
~/locale
'
;
import
systemNote
from
'
~/vue_shared/components/notes/system_note.vue
'
;
import
icon
from
'
~/vue_shared/components/icon.vue
'
;
import
Flash
from
'
../../flash
'
;
import
Flash
from
'
../../flash
'
;
import
{
SYSTEM_NOTE
}
from
'
../constants
'
;
import
{
SYSTEM_NOTE
}
from
'
../constants
'
;
import
userAvatarLink
from
'
../../vue_shared/components/user_avatar/user_avatar_link.vue
'
;
import
userAvatarLink
from
'
../../vue_shared/components/user_avatar/user_avatar_link.vue
'
;
import
noteableNote
from
'
./noteable_note.vue
'
;
import
noteableNote
from
'
./noteable_note.vue
'
;
import
noteHeader
from
'
./note_header.vue
'
;
import
noteHeader
from
'
./note_header.vue
'
;
import
toggleRepliesWidget
from
'
./toggle_replies_widget.vue
'
;
import
noteSignedOutWidget
from
'
./note_signed_out_widget.vue
'
;
import
noteSignedOutWidget
from
'
./note_signed_out_widget.vue
'
;
import
noteEditedText
from
'
./note_edited_text.vue
'
;
import
noteEditedText
from
'
./note_edited_text.vue
'
;
import
noteForm
from
'
./note_form.vue
'
;
import
noteForm
from
'
./note_form.vue
'
;
...
@@ -26,6 +26,7 @@ import tooltip from '../../vue_shared/directives/tooltip';
...
@@ -26,6 +26,7 @@ import tooltip from '../../vue_shared/directives/tooltip';
export
default
{
export
default
{
name
:
'
NoteableDiscussion
'
,
name
:
'
NoteableDiscussion
'
,
components
:
{
components
:
{
icon
,
noteableNote
,
noteableNote
,
diffWithNote
,
diffWithNote
,
userAvatarLink
,
userAvatarLink
,
...
@@ -33,6 +34,7 @@ export default {
...
@@ -33,6 +34,7 @@ export default {
noteSignedOutWidget
,
noteSignedOutWidget
,
noteEditedText
,
noteEditedText
,
noteForm
,
noteForm
,
toggleRepliesWidget
,
placeholderNote
,
placeholderNote
,
placeholderSystemNote
,
placeholderSystemNote
,
systemNote
,
systemNote
,
...
@@ -46,11 +48,6 @@ export default {
...
@@ -46,11 +48,6 @@ export default {
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
renderHeader
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
renderDiffFile
:
{
renderDiffFile
:
{
type
:
Boolean
,
type
:
Boolean
,
required
:
false
,
required
:
false
,
...
@@ -72,6 +69,7 @@ export default {
...
@@ -72,6 +69,7 @@ export default {
isReplying
:
false
,
isReplying
:
false
,
isResolving
:
false
,
isResolving
:
false
,
resolveAsThread
:
true
,
resolveAsThread
:
true
,
isRepliesCollapsed
:
(
!
this
.
discussion
.
diff_discussion
&&
this
.
discussion
.
resolved
)
||
false
,
};
};
},
},
computed
:
{
computed
:
{
...
@@ -112,6 +110,15 @@ export default {
...
@@ -112,6 +110,15 @@ export default {
newNotePath
()
{
newNotePath
()
{
return
this
.
getNoteableData
.
create_note_path
;
return
this
.
getNoteableData
.
create_note_path
;
},
},
hasReplies
()
{
return
this
.
discussion
.
notes
.
length
>
1
;
},
initialDiscussion
()
{
return
this
.
discussion
.
notes
.
slice
(
0
,
1
)[
0
];
},
replies
()
{
return
this
.
discussion
.
notes
.
slice
(
1
);
},
lastUpdatedBy
()
{
lastUpdatedBy
()
{
const
{
notes
}
=
this
.
discussion
;
const
{
notes
}
=
this
.
discussion
;
...
@@ -147,6 +154,12 @@ export default {
...
@@ -147,6 +154,12 @@ export default {
return
diffDiscussion
&&
diffFile
&&
this
.
renderDiffFile
;
return
diffDiscussion
&&
diffFile
&&
this
.
renderDiffFile
;
},
},
shouldGroupReplies
()
{
return
!
this
.
shouldRenderDiffs
&&
!
this
.
transformedDiscussion
.
diffDiscussion
;
},
shouldRenderHeader
()
{
return
this
.
shouldRenderDiffs
;
},
wrapperComponent
()
{
wrapperComponent
()
{
return
this
.
shouldRenderDiffs
?
diffWithNote
:
'
div
'
;
return
this
.
shouldRenderDiffs
?
diffWithNote
:
'
div
'
;
},
},
...
@@ -160,6 +173,22 @@ export default {
...
@@ -160,6 +173,22 @@ export default {
wrapperClass
()
{
wrapperClass
()
{
return
this
.
isDiffDiscussion
?
''
:
'
card discussion-wrapper
'
;
return
this
.
isDiffDiscussion
?
''
:
'
card discussion-wrapper
'
;
},
},
componentClassName
()
{
if
(
this
.
shouldRenderDiffs
)
{
if
(
!
this
.
lastUpdatedAt
&&
!
this
.
discussion
.
resolved
)
{
return
'
unresolved
'
;
}
}
return
''
;
},
shouldShowDiscussions
()
{
const
isExpanded
=
this
.
discussion
.
expanded
;
const
{
diffDiscussion
,
resolved
}
=
this
.
transformedDiscussion
;
const
isResolvedNonDiffDiscussion
=
!
diffDiscussion
&&
resolved
;
return
isExpanded
||
this
.
alwaysExpanded
||
isResolvedNonDiffDiscussion
;
},
},
},
watch
:
{
watch
:
{
isReplying
()
{
isReplying
()
{
...
@@ -173,10 +202,6 @@ export default {
...
@@ -173,10 +202,6 @@ export default {
}
}
},
},
},
},
created
()
{
this
.
resolveDiscussionsSvg
=
resolveDiscussionsSvg
;
this
.
nextDiscussionsSvg
=
nextDiscussionsSvg
;
},
methods
:
{
methods
:
{
...
mapActions
([
...
mapActions
([
'
saveNote
'
,
'
saveNote
'
,
...
@@ -207,6 +232,9 @@ export default {
...
@@ -207,6 +232,9 @@ export default {
toggleDiscussionHandler
()
{
toggleDiscussionHandler
()
{
this
.
toggleDiscussion
({
discussionId
:
this
.
discussion
.
id
});
this
.
toggleDiscussion
({
discussionId
:
this
.
discussion
.
id
});
},
},
toggleReplies
()
{
this
.
isRepliesCollapsed
=
!
this
.
isRepliesCollapsed
;
},
showReplyForm
()
{
showReplyForm
()
{
this
.
isReplying
=
true
;
this
.
isReplying
=
true
;
},
},
...
@@ -274,8 +302,20 @@ Please check your network connection and try again.`;
...
@@ -274,8 +302,20 @@ Please check your network connection and try again.`;
</
script
>
</
script
>
<
template
>
<
template
>
<li
class=
"note note-discussion timeline-entry"
>
<li
class=
"note note-discussion timeline-entry"
:class=
"componentClassName"
>
<div
class=
"timeline-entry-inner"
>
<div
class=
"timeline-entry-inner"
>
<div
class=
"timeline-content"
>
<div
:data-discussion-id=
"transformedDiscussion.discussion_id"
class=
"discussion js-discussion-container"
>
<div
v-if=
"shouldRenderHeader"
class=
"discussion-header note-wrapper"
>
<div
class=
"timeline-icon"
>
<div
class=
"timeline-icon"
>
<user-avatar-link
<user-avatar-link
v-if=
"author"
v-if=
"author"
...
@@ -285,15 +325,6 @@ Please check your network connection and try again.`;
...
@@ -285,15 +325,6 @@ Please check your network connection and try again.`;
:img-size=
"40"
:img-size=
"40"
/>
/>
</div>
</div>
<div
class=
"timeline-content"
>
<div
:data-discussion-id=
"transformedDiscussion.discussion_id"
class=
"discussion js-discussion-container"
>
<div
v-if=
"renderHeader"
class=
"discussion-header"
>
<note-header
<note-header
:author=
"author"
:author=
"author"
:created-at=
"transformedDiscussion.created_at"
:created-at=
"transformedDiscussion.created_at"
...
@@ -339,7 +370,7 @@ Please check your network connection and try again.`;
...
@@ -339,7 +370,7 @@ Please check your network connection and try again.`;
/>
/>
</div>
</div>
<div
<div
v-if=
"
discussion.expanded || alwaysExpanded
"
v-if=
"
shouldShowDiscussions
"
class=
"discussion-body"
>
class=
"discussion-body"
>
<component
<component
:is=
"wrapperComponent"
:is=
"wrapperComponent"
...
@@ -348,6 +379,35 @@ Please check your network connection and try again.`;
...
@@ -348,6 +379,35 @@ Please check your network connection and try again.`;
>
>
<div
class=
"discussion-notes"
>
<div
class=
"discussion-notes"
>
<ul
class=
"notes"
>
<ul
class=
"notes"
>
<
template
v-if=
"shouldGroupReplies"
>
<component
:is=
"componentName(initialDiscussion)"
:note=
"componentData(initialDiscussion)"
@
handleDeleteNote=
"deleteNoteHandler"
>
<slot
slot=
"avatar-badge"
name=
"avatar-badge"
>
</slot>
</component>
<toggle-replies-widget
v-if=
"hasReplies"
:collapsed=
"isRepliesCollapsed"
:replies=
"replies"
@
toggle=
"toggleReplies"
/>
<template
v-if=
"!isRepliesCollapsed"
>
<component
:is=
"componentName(note)"
v-for=
"note in replies"
:key=
"note.id"
:note=
"componentData(note)"
@
handleDeleteNote=
"deleteNoteHandler"
/>
</
template
>
</template>
<
template
v-else
>
<component
<component
:is=
"componentName(note)"
:is=
"componentName(note)"
v-for=
"(note, index) in discussion.notes"
v-for=
"(note, index) in discussion.notes"
...
@@ -362,31 +422,27 @@ Please check your network connection and try again.`;
...
@@ -362,31 +422,27 @@ Please check your network connection and try again.`;
>
>
</slot>
</slot>
</component>
</component>
</
template
>
</ul>
</ul>
<div
<div
v-if=
"!isRepliesCollapsed"
:class=
"{ 'is-replying': isReplying }"
:class=
"{ 'is-replying': isReplying }"
class=
"discussion-reply-holder"
class=
"discussion-reply-holder"
>
>
<
template
v-if=
"!isReplying && canReply"
>
<
template
v-if=
"!isReplying && canReply"
>
<div
<div
class=
"discussion-with-resolve-btn"
>
class=
"btn-group d-flex discussion-with-resolve-btn"
role=
"group"
>
<div
class=
"btn-group w-100"
role=
"group"
>
<button
<button
type=
"button"
type=
"button"
class=
"js-vue-discussion-reply btn btn-text-field mr-2 qa-discussion-reply"
class=
"js-vue-discussion-reply btn btn-text-field mr-2 qa-discussion-reply"
title=
"Add a reply"
title=
"Add a reply"
@
click=
"showReplyForm"
>
Reply...
</button>
@
click=
"showReplyForm"
</div>
>
<div
Reply...
v-if=
"discussion.resolvable"
</button>
class=
"btn-group"
<div
v-if=
"discussion.resolvable"
>
role=
"group"
>
<button
<button
type=
"button"
type=
"button"
class=
"btn btn-default"
class=
"btn btn-default
mx-sm-2
"
@
click=
"resolveHandler()"
@
click=
"resolveHandler()"
>
>
<i
<i
...
@@ -414,7 +470,7 @@ Please check your network connection and try again.`;
...
@@ -414,7 +470,7 @@ Please check your network connection and try again.`;
btn-default discussion-create-issue-btn"
btn-default discussion-create-issue-btn"
data-container=
"body"
data-container=
"body"
>
>
<
span
v-html=
"resolveDiscussionsSvg"
></span
>
<
icon
name=
"issue-new"
/
>
</a>
</a>
</div>
</div>
<div
<div
...
@@ -428,7 +484,7 @@ Please check your network connection and try again.`;
...
@@ -428,7 +484,7 @@ Please check your network connection and try again.`;
data-container=
"body"
data-container=
"body"
@
click=
"jumpToNextDiscussion"
@
click=
"jumpToNextDiscussion"
>
>
<
span
v-html=
"nextDiscussionsSvg"
></span
>
<
icon
name=
"comment-next"
/
>
</button>
</button>
</div>
</div>
</div>
</div>
...
...
app/assets/javascripts/notes/components/noteable_note.vue
View file @
cd5ddc4f
...
@@ -173,7 +173,7 @@ export default {
...
@@ -173,7 +173,7 @@ export default {
:class=
"classNameBindings"
:class=
"classNameBindings"
:data-award-url=
"note.toggle_award_path"
:data-award-url=
"note.toggle_award_path"
:data-note-id=
"note.id"
:data-note-id=
"note.id"
class=
"note timeline-entry"
class=
"note timeline-entry
note-wrapper
"
>
>
<div
class=
"timeline-entry-inner"
>
<div
class=
"timeline-entry-inner"
>
<div
class=
"timeline-icon"
>
<div
class=
"timeline-icon"
>
...
@@ -196,6 +196,7 @@ export default {
...
@@ -196,6 +196,7 @@ export default {
:author=
"author"
:author=
"author"
:created-at=
"note.created_at"
:created-at=
"note.created_at"
:note-id=
"note.id"
:note-id=
"note.id"
action-text=
"commented"
/>
/>
<note-actions
<note-actions
:author-id=
"author.id"
:author-id=
"author.id"
...
...
app/assets/javascripts/notes/components/toggle_replies_widget.vue
0 → 100644
View file @
cd5ddc4f
<
script
>
import
_
from
'
underscore
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
UserAvatarLink
from
'
~/vue_shared/components/user_avatar/user_avatar_link.vue
'
;
import
TimeAgoTooltip
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
export
default
{
components
:
{
Icon
,
UserAvatarLink
,
TimeAgoTooltip
,
},
props
:
{
collapsed
:
{
type
:
Boolean
,
required
:
true
,
},
replies
:
{
type
:
Array
,
required
:
true
,
},
},
computed
:
{
lastReply
()
{
return
this
.
replies
[
this
.
replies
.
length
-
1
];
},
uniqueAuthors
()
{
const
authors
=
this
.
replies
.
map
(
reply
=>
reply
.
author
||
{});
return
_
.
uniq
(
authors
,
author
=>
author
.
username
);
},
className
()
{
return
this
.
collapsed
?
'
collapsed
'
:
'
expanded
'
;
},
},
methods
:
{
toggle
()
{
this
.
$emit
(
'
toggle
'
);
},
},
};
</
script
>
<
template
>
<li
:class=
"className"
class=
"replies-toggle"
>
<template
v-if=
"collapsed"
>
<icon
name=
"chevron-right"
@
click.native=
"toggle"
/>
<div>
<user-avatar-link
v-for=
"author in uniqueAuthors"
:key=
"author.username"
:link-href=
"author.path"
:img-alt=
"author.name"
:img-src=
"author.avatar_url"
:img-size=
"26"
:tooltip-text=
"author.name"
tooltip-placement=
"bottom"
/>
</div>
<button
class=
"btn btn-link js-replies-text"
type=
"button"
@
click=
"toggle"
>
{{
replies
.
length
}}
{{
n__
(
'
reply
'
,
'
replies
'
,
replies
.
length
)
}}
</button>
{{
__
(
'
Last reply by
'
)
}}
<a
:href=
"lastReply.author.path"
class=
"btn btn-link author-link"
>
{{
lastReply
.
author
.
name
}}
</a>
<time-ago-tooltip
:time=
"lastReply.created_at"
tooltip-placement=
"bottom"
/>
</
template
>
<span
v-else
class=
"collapse-replies-btn js-collapse-replies"
@
click=
"toggle"
>
<icon
name=
"chevron-down"
/>
{{ s__('Notes|Collapse replies') }}
</span>
</li>
</template>
app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue
View file @
cd5ddc4f
...
@@ -10,7 +10,7 @@ export default {
...
@@ -10,7 +10,7 @@ export default {
</
script
>
</
script
>
<
template
>
<
template
>
<li
class=
"timeline-entry note"
>
<li
class=
"timeline-entry note
note-wrapper
"
>
<div
class=
"timeline-entry-inner"
>
<div
class=
"timeline-entry-inner"
>
<div
class=
"timeline-icon"
>
<div
class=
"timeline-icon"
>
</div>
</div>
...
...
app/assets/javascripts/vue_shared/components/notes/system_note.vue
View file @
cd5ddc4f
...
@@ -76,7 +76,7 @@ export default {
...
@@ -76,7 +76,7 @@ export default {
<li
<li
:id=
"noteAnchorId"
:id=
"noteAnchorId"
:class=
"
{ target: isTargetNote }"
:class=
"
{ target: isTargetNote }"
class="note system-note timeline-entry">
class="note system-note timeline-entry
note-wrapper
">
<div
class=
"timeline-entry-inner"
>
<div
class=
"timeline-entry-inner"
>
<div
<div
class=
"timeline-icon"
class=
"timeline-icon"
...
...
app/assets/stylesheets/framework/awards.scss
View file @
cd5ddc4f
...
@@ -148,11 +148,8 @@
...
@@ -148,11 +148,8 @@
.award-control-icon
svg
{
.award-control-icon
svg
{
background
:
$award-emoji-positive-add-bg
;
background
:
$award-emoji-positive-add-bg
;
path
{
fill
:
$award-emoji-positive-add-lines
;
fill
:
$award-emoji-positive-add-lines
;
}
}
}
.award-control-icon-neutral
{
.award-control-icon-neutral
{
opacity
:
0
;
opacity
:
0
;
...
...
app/assets/stylesheets/framework/buttons.scss
View file @
cd5ddc4f
...
@@ -222,6 +222,25 @@
...
@@ -222,6 +222,25 @@
}
}
}
}
&
.btn-text-field
{
width
:
100%
;
text-align
:
left
;
padding
:
6px
16px
;
border-color
:
$border-color
;
color
:
$gray-darkest
;
background-color
:
$gray-light
;
&
:hover
,
&
:active
,
&
:focus
{
cursor
:
text
;
box-shadow
:
none
;
border-color
:
lighten
(
$blue-300
,
20%
);
color
:
$gray-darkest
;
background-color
:
$gray-light
;
}
}
&.
dot-highlight
:
:
after
{
&.
dot-highlight
:
:
after
{
content
:
''
;
content
:
''
;
background-color
:
$blue-500
;
background-color
:
$blue-500
;
...
@@ -339,25 +358,6 @@
...
@@ -339,25 +358,6 @@
}
}
}
}
.btn-text-field
{
width
:
100%
;
text-align
:
left
;
padding
:
6px
16px
;
border-color
:
$border-color
;
color
:
$gray-darkest
;
background-color
:
$gray-light
;
&
:hover
,
&
:active
,
&
:focus
{
cursor
:
text
;
box-shadow
:
none
;
border-color
:
lighten
(
$blue-300
,
20%
);
color
:
$gray-darkest
;
background-color
:
$gray-light
;
}
}
.btn-build
{
.btn-build
{
margin-left
:
10px
;
margin-left
:
10px
;
...
...
app/assets/stylesheets/framework/files.scss
View file @
cd5ddc4f
...
@@ -36,7 +36,6 @@
...
@@ -36,7 +36,6 @@
text-align
:
left
;
text-align
:
left
;
padding
:
10px
$gl-padding
;
padding
:
10px
$gl-padding
;
word-wrap
:
break-word
;
word-wrap
:
break-word
;
border-radius
:
$border-radius-default
$border-radius-default
0
0
;
&
.file-title-clear
{
&
.file-title-clear
{
padding-left
:
0
;
padding-left
:
0
;
...
...
app/assets/stylesheets/framework/timeline.scss
View file @
cd5ddc4f
.timeline
{
.timeline
{
@include
basic-list
;
margin
:
0
;
margin
:
0
;
padding
:
0
;
padding
:
0
;
list-style
:
none
;
&
:
:
before
{
&
:
:
before
{
@include
notes-media
(
'max'
,
map-get
(
$grid-breakpoints
,
sm
))
{
@include
notes-media
(
'max'
,
map-get
(
$grid-breakpoints
,
sm
))
{
...
@@ -26,10 +26,8 @@
...
@@ -26,10 +26,8 @@
}
}
.timeline-entry
{
.timeline-entry
{
border-color
:
$white-normal
;
color
:
$gl-text-color
;
color
:
$gl-text-color
;
border-bottom
:
1px
solid
$border-white-light
;
background-color
:
$white-light
;
background
:
$white-light
;
.timeline-entry-inner
{
.timeline-entry-inner
{
position
:
relative
;
position
:
relative
;
...
...
app/assets/stylesheets/pages/diff.scss
View file @
cd5ddc4f
...
@@ -59,6 +59,7 @@
...
@@ -59,6 +59,7 @@
margin
:
0
;
margin
:
0
;
padding
:
0
;
padding
:
0
;
table-layout
:
fixed
;
table-layout
:
fixed
;
border-radius
:
0
0
$border-radius-default
$border-radius-default
;
.diff-line-num
{
.diff-line-num
{
width
:
50px
;
width
:
50px
;
...
@@ -859,7 +860,7 @@
...
@@ -859,7 +860,7 @@
}
}
.diff-file
.note-container
>
.new-note
,
.diff-file
.note-container
>
.new-note
,
.note-container
.discussion-notes
{
.note-container
.discussion-notes
.diff-discussions
{
margin-left
:
100px
;
margin-left
:
100px
;
border-left
:
1px
solid
$white-normal
;
border-left
:
1px
solid
$white-normal
;
}
}
...
...
app/assets/stylesheets/pages/note_form.scss
View file @
cd5ddc4f
...
@@ -239,6 +239,7 @@
...
@@ -239,6 +239,7 @@
.discussion-reply-holder
{
.discussion-reply-holder
{
background-color
:
$white-light
;
background-color
:
$white-light
;
padding
:
10px
16px
;
padding
:
10px
16px
;
border-radius
:
0
0
$border-radius-default
$border-radius-default
;
&
.is-replying
{
&
.is-replying
{
padding-bottom
:
$gl-padding
;
padding-bottom
:
$gl-padding
;
...
@@ -247,10 +248,15 @@
...
@@ -247,10 +248,15 @@
}
}
.discussion-with-resolve-btn
{
.discussion-with-resolve-btn
{
@include
media-breakpoint-up
(
sm
)
{
display
:
flex
;
}
.discussion-actions
{
.discussion-actions
{
display
:
table
;
display
:
table
;
.btn-default
path
{
svg
{
fill
:
$gray-darkest
;
fill
:
$gray-darkest
;
}
}
...
@@ -270,6 +276,12 @@
...
@@ -270,6 +276,12 @@
.btn
{
.btn
{
width
:
100%
;
width
:
100%
;
}
}
.btn-text-field
{
@include
media-breakpoint-down
(
xs
)
{
margin-bottom
:
$gl-padding-8
;
}
}
}
}
.discussion-notes-count
{
.discussion-notes-count
{
...
...
app/assets/stylesheets/pages/notes.scss
View file @
cd5ddc4f
/**
$system-note-icon-size
:
32px
;
* Notes
$system-note-svg-size
:
16px
;
*/
$note-form-margin-left
:
70px
;
@mixin
vertical-line
(
$left
)
{
&
:
:
before
{
content
:
''
;
border-left
:
2px
solid
$theme-gray-100
;
position
:
absolute
;
top
:
0
;
bottom
:
0
;
left
:
$left
;
}
}
.note-wrapper
{
padding
:
$gl-padding
;
}
.issuable-discussion
{
.notes.timeline
>
.timeline-entry
{
border
:
1px
solid
$border-color
;
border-radius
:
$border-radius-default
;
margin
:
$gl-padding
0
;
@-webkit-keyframes
targe3-note
{
&
.system-note
,
fro
m
{
&
.note-for
m
{
background
:
$note-targe3-outside
;
border
:
0
;
}
}
50
%
{
&
.note-form
{
background
:
$note-targe3-inside
;
margin-left
:
0
;
@include
notes-media
(
'min'
,
map-get
(
$grid-breakpoints
,
md
))
{
margin-left
:
$note-form-margin-left
;
}
}
to
{
.timeline-icon
{
background
:
$note-targe3-outside
;
@include
notes-media
(
'min'
,
map-get
(
$grid-breakpoints
,
sm
))
{
margin-left
:
-
$note-icon-gutter-width
;
}
}
.timeline-content
{
margin-left
:
0
;
}
}
.notes_content
{
border
:
0
;
border-top
:
1px
solid
$border-color
;
}
}
}
}
}
ul
.notes
{
.main-notes-list
{
@include
vertical-line
(
39px
);
}
.notes
{
display
:
block
;
display
:
block
;
list-style
:
none
;
list-style
:
none
;
margin
:
0
;
margin
:
0
;
padding
:
0
;
padding
:
0
;
position
:
relative
;
>
.note-discussion
{
.card
{
border
:
0
;
}
li
.note
{
border-bottom
:
1px
solid
$border-color
;
&
:first-child
{
border-radius
:
$border-radius-default
$border-radius-default
0
0
;
}
}
}
.replies-toggle
{
background-color
:
$gray-light
;
padding
:
$gl-padding-8
$gl-padding
;
.collapse-replies-btn
:hover
{
color
:
$blue-600
;
}
&
.expanded
{
border-bottom
:
1px
solid
$border-color
;
span
{
cursor
:
pointer
;
}
svg
{
position
:
relative
;
top
:
3px
;
}
}
&
.collapsed
{
color
:
$gl-text-color-secondary
;
svg
{
float
:
left
;
position
:
relative
;
top
:
$gl-padding-4
;
margin-right
:
$gl-padding-8
;
cursor
:
pointer
;
}
img
{
margin
:
-2px
4px
0
0
;
}
.author-link
{
color
:
$gl-text-color
;
}
}
.user-avatar-link
{
&
:last-child
img
{
margin-right
:
$gl-padding-8
;
}
}
.btn-link
{
border
:
0
;
vertical-align
:
baseline
;
}
}
.note-created-ago
,
.note-created-ago
,
.note-updated-at
{
.note-updated-at
{
...
@@ -28,8 +137,6 @@ ul.notes {
...
@@ -28,8 +137,6 @@ ul.notes {
}
}
.discussion-body
{
.discussion-body
{
padding-top
:
8px
;
.card
{
.card
{
margin-bottom
:
0
;
margin-bottom
:
0
;
}
}
...
@@ -46,21 +153,10 @@ ul.notes {
...
@@ -46,21 +153,10 @@ ul.notes {
}
}
>
li
{
>
li
{
// .timeline-entry
padding
:
0
;
display
:
block
;
display
:
block
;
position
:
relative
;
position
:
relative
;
border-bottom
:
0
;
border-bottom
:
0
;
@include
notes-media
(
'min'
,
map-get
(
$grid-breakpoints
,
sm
))
{
padding-left
:
$note-icon-gutter-width
;
}
.timeline-entry-inner
{
padding
:
$gl-padding
$gl-btn-padding
;
border-bottom
:
1px
solid
$white-normal
;
}
&
:target
,
&
:target
,
&
.target
{
&
.target
{
border-bottom
:
1px
solid
$white-normal
;
border-bottom
:
1px
solid
$white-normal
;
...
@@ -75,23 +171,10 @@ ul.notes {
...
@@ -75,23 +171,10 @@ ul.notes {
}
}
}
}
.timeline-icon
{
@include
notes-media
(
'min'
,
map-get
(
$grid-breakpoints
,
sm
))
{
margin-left
:
-
$note-icon-gutter-width
;
}
}
.timeline-content
{
margin-left
:
$note-icon-gutter-width
;
@include
notes-media
(
'min'
,
map-get
(
$grid-breakpoints
,
sm
))
{
margin-left
:
0
;
}
}
&
.being-posted
{
&
.being-posted
{
pointer-events
:
none
;
pointer-events
:
none
;
opacity
:
0
.5
;
opacity
:
0
.5
;
padding
:
$gl-padding
;
.dummy-avatar
{
.dummy-avatar
{
background-color
:
$gl-gray-200
;
background-color
:
$gl-gray-200
;
...
@@ -104,12 +187,6 @@ ul.notes {
...
@@ -104,12 +187,6 @@ ul.notes {
}
}
}
}
&
.note-discussion
{
.timeline-entry-inner
{
padding
:
$gl-padding
10px
;
}
}
.editing-spinner
{
.editing-spinner
{
display
:
none
;
display
:
none
;
}
}
...
@@ -191,8 +268,9 @@ ul.notes {
...
@@ -191,8 +268,9 @@ ul.notes {
}
}
.system-note
{
.system-note
{
font-size
:
14px
;
padding
:
6px
$gl-padding-24
;
clear
:
both
;
margin
:
$gl-padding-24
0
;
background-color
:
transparent
;
.note-header-info
{
.note-header-info
{
padding-bottom
:
0
;
padding-bottom
:
0
;
...
@@ -225,17 +303,21 @@ ul.notes {
...
@@ -225,17 +303,21 @@ ul.notes {
.timeline-icon
{
.timeline-icon
{
float
:
left
;
float
:
left
;
display
:
flex
;
@include
notes-media
(
'min'
,
map-get
(
$grid-breakpoints
,
sm
))
{
align-items
:
center
;
margin-left
:
0
;
background-color
:
$white-light
;
width
:
auto
;
width
:
$system-note-icon-size
;
}
height
:
$system-note-icon-size
;
border
:
1px
solid
$border-color
;
border-radius
:
$system-note-icon-size
;
margin
:
-6px
$gl-padding
0
0
;
svg
{
svg
{
width
:
16px
;
width
:
$system-note-svg-size
;
height
:
16px
;
height
:
$system-note-svg-size
;
fill
:
$gray-darkest
;
fill
:
$gray-darkest
;
margin-top
:
2px
;
display
:
block
;
margin
:
0
auto
;
}
}
}
}
...
@@ -302,10 +384,17 @@ ul.notes {
...
@@ -302,10 +384,17 @@ ul.notes {
.discussion-body
.diff-file
{
.discussion-body
.diff-file
{
.file-title
{
.file-title
{
cursor
:
default
;
cursor
:
default
;
line-height
:
42px
;
padding
:
0
$gl-padding
;
border-top
:
1px
solid
$border-color
;
&
:hover
{
&
:hover
{
background-color
:
$gray-light
;
background-color
:
$gray-light
;
}
}
.btn-clipboard
{
top
:
10px
;
}
}
}
.line_content
{
.line_content
{
...
@@ -320,6 +409,23 @@ ul.notes {
...
@@ -320,6 +409,23 @@ ul.notes {
}
}
}
}
.discussion-notes
{
&
:not
(
:first-child
)
{
border-top
:
1px
solid
$white-normal
;
margin-top
:
20px
;
}
&
:not
(
:last-child
)
{
border-bottom
:
1px
solid
$white-normal
;
margin-bottom
:
20px
;
}
.system-note
{
margin
:
0
;
padding
:
$gl-padding
;
}
}
// Merge request notes in diffs
// Merge request notes in diffs
// Diff is inline
// Diff is inline
.notes_content
.note-header
.note-headline-light
{
.notes_content
.note-header
.note-headline-light
{
...
@@ -335,7 +441,6 @@ ul.notes {
...
@@ -335,7 +441,6 @@ ul.notes {
border-left
:
0
;
border-left
:
0
;
&
.notes_content
{
&
.notes_content
{
background-color
:
$gray-light
;
border-width
:
1px
0
;
border-width
:
1px
0
;
padding
:
0
;
padding
:
0
;
vertical-align
:
top
;
vertical-align
:
top
;
...
@@ -349,18 +454,6 @@ ul.notes {
...
@@ -349,18 +454,6 @@ ul.notes {
}
}
}
}
.discussion-notes
{
&
:not
(
:first-child
)
{
border-top
:
1px
solid
$white-normal
;
margin-top
:
20px
;
}
&
:not
(
:last-child
)
{
border-bottom
:
1px
solid
$white-normal
;
margin-bottom
:
20px
;
}
}
.notes
{
.notes
{
background-color
:
$white-light
;
background-color
:
$white-light
;
}
}
...
@@ -374,6 +467,30 @@ ul.notes {
...
@@ -374,6 +467,30 @@ ul.notes {
}
}
}
}
.diffs
{
.discussion-notes
{
margin-left
:
0
;
border-left
:
0
;
.notes
{
position
:
relative
;
@include
vertical-line
(
52px
);
}
}
.note-wrapper
{
margin
:
$gl-padding
;
border
:
1px
solid
$border-color
;
border-radius
:
$border-radius-default
;
}
.discussion-reply-holder
{
border-radius
:
0
0
$border-radius-default
$border-radius-default
;
border-top
:
1px
solid
$border-color
;
position
:
relative
;
}
}
.discussion-header
,
.discussion-header
,
.note-header-info
{
.note-header-info
{
a
{
a
{
...
@@ -399,7 +516,17 @@ ul.notes {
...
@@ -399,7 +516,17 @@ ul.notes {
}
}
.discussion-header
{
.discussion-header
{
font-size
:
14px
;
min-height
:
72px
;
.note-header-info
{
padding-bottom
:
0
;
}
}
.unresolved
{
.note-header-info
{
margin-top
:
$gl-padding-8
;
}
}
}
.note-header
{
.note-header
{
...
@@ -409,7 +536,7 @@ ul.notes {
...
@@ -409,7 +536,7 @@ ul.notes {
.note-header-info
{
.note-header-info
{
min-width
:
0
;
min-width
:
0
;
padding-bottom
:
8px
;
padding-bottom
:
$gl-padding-8
;
&
.discussion
{
&
.discussion
{
padding-bottom
:
0
;
padding-bottom
:
0
;
...
@@ -471,9 +598,18 @@ ul.notes {
...
@@ -471,9 +598,18 @@ ul.notes {
margin-left
:
10px
;
margin-left
:
10px
;
color
:
$gray-darkest
;
color
:
$gray-darkest
;
@include
media-breakpoint-down
(
xs
)
{
width
:
100%
;
margin
:
$gl-padding-8
0
;
}
.btn-group
>
.discussion-next-btn
{
.btn-group
>
.discussion-next-btn
{
margin-left
:
-1px
;
margin-left
:
-1px
;
}
}
svg
{
height
:
15px
;
}
}
}
.note-actions
{
.note-actions
{
...
@@ -585,19 +721,6 @@ ul.notes {
...
@@ -585,19 +721,6 @@ ul.notes {
z-index
:
10
;
z-index
:
10
;
}
}
.discussion-body
,
.diff-file
{
.notes
.note
{
border-bottom
:
1px
solid
$white-normal
;
.timeline-entry-inner
{
padding-left
:
$gl-padding
;
padding-right
:
$gl-padding
;
border-bottom
:
0
;
}
}
}
.disabled-comment
{
.disabled-comment
{
background-color
:
$gray-light
;
background-color
:
$gray-light
;
border-radius
:
$border-radius-base
;
border-radius
:
$border-radius-base
;
...
@@ -634,7 +757,7 @@ ul.notes {
...
@@ -634,7 +757,7 @@ ul.notes {
}
}
.btn
{
.btn
{
svg
path
{
svg
{
fill
:
$gray-darkest
;
fill
:
$gray-darkest
;
}
}
...
@@ -659,7 +782,7 @@ ul.notes {
...
@@ -659,7 +782,7 @@ ul.notes {
.line-resolve-all
{
.line-resolve-all
{
vertical-align
:
middle
;
vertical-align
:
middle
;
display
:
inline-block
;
display
:
inline-block
;
padding
:
5px
10px
6
px
;
padding
:
6px
10
px
;
background-color
:
$gray-light
;
background-color
:
$gray-light
;
border
:
1px
solid
$border-color
;
border
:
1px
solid
$border-color
;
border-radius
:
$border-radius-default
;
border-radius
:
$border-radius-default
;
...
...
app/views/shared/notes/_notes_with_form.html.haml
View file @
cd5ddc4f
...
@@ -7,8 +7,8 @@
...
@@ -7,8 +7,8 @@
=
render
'shared/notes/edit_form'
,
project:
@project
=
render
'shared/notes/edit_form'
,
project:
@project
-
if
can_create_note?
-
if
can_create_note?
%ul
.notes.notes-form.timeline
.notes.notes-form.timeline
%li
.timeline-entry
.timeline-entry
.timeline-entry-inner
.timeline-entry-inner
.flash-container.timeline-content
.flash-container.timeline-content
...
...
locale/gitlab.pot
View file @
cd5ddc4f
...
@@ -3591,6 +3591,9 @@ msgstr ""
...
@@ -3591,6 +3591,9 @@ msgstr ""
msgid "Last edited by %{name}"
msgid "Last edited by %{name}"
msgstr ""
msgstr ""
msgid "Last reply by"
msgstr ""
msgid "Last update"
msgid "Last update"
msgstr ""
msgstr ""
...
@@ -4192,6 +4195,9 @@ msgstr ""
...
@@ -4192,6 +4195,9 @@ msgstr ""
msgid "Notes|Are you sure you want to cancel creating this comment?"
msgid "Notes|Are you sure you want to cancel creating this comment?"
msgstr ""
msgstr ""
msgid "Notes|Collapse replies"
msgstr ""
msgid "Notes|Show all activity"
msgid "Notes|Show all activity"
msgstr ""
msgstr ""
...
@@ -7568,6 +7574,11 @@ msgstr ""
...
@@ -7568,6 +7574,11 @@ msgstr ""
msgid "remove due date"
msgid "remove due date"
msgstr ""
msgstr ""
msgid "reply"
msgid_plural "replies"
msgstr[0] ""
msgstr[1] ""
msgid "source"
msgid "source"
msgstr ""
msgstr ""
...
...
spec/javascripts/notes/components/noteable_discussion_spec.js
View file @
cd5ddc4f
...
@@ -3,6 +3,7 @@ import createStore from '~/notes/stores';
...
@@ -3,6 +3,7 @@ import createStore from '~/notes/stores';
import
noteableDiscussion
from
'
~/notes/components/noteable_discussion.vue
'
;
import
noteableDiscussion
from
'
~/notes/components/noteable_discussion.vue
'
;
import
'
~/behaviors/markdown/render_gfm
'
;
import
'
~/behaviors/markdown/render_gfm
'
;
import
{
noteableDataMock
,
discussionMock
,
notesDataMock
}
from
'
../mock_data
'
;
import
{
noteableDataMock
,
discussionMock
,
notesDataMock
}
from
'
../mock_data
'
;
import
mockDiffFile
from
'
../../diffs/mock_data/diff_file
'
;
const
discussionWithTwoUnresolvedNotes
=
'
merge_requests/resolved_diff_discussion.json
'
;
const
discussionWithTwoUnresolvedNotes
=
'
merge_requests/resolved_diff_discussion.json
'
;
...
@@ -33,9 +34,20 @@ describe('noteable_discussion component', () => {
...
@@ -33,9 +34,20 @@ describe('noteable_discussion component', () => {
expect
(
vm
.
$el
.
querySelector
(
'
.user-avatar-link
'
)).
not
.
toBeNull
();
expect
(
vm
.
$el
.
querySelector
(
'
.user-avatar-link
'
)).
not
.
toBeNull
();
});
});
it
(
'
should not render discussion header for non diff discussions
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.discussion-header
'
)).
toBeNull
();
});
it
(
'
should render discussion header
'
,
()
=>
{
it
(
'
should render discussion header
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.discussion-header
'
)).
not
.
toBeNull
();
const
discussion
=
{
...
discussionMock
};
expect
(
vm
.
$el
.
querySelector
(
'
.notes
'
).
children
.
length
).
toEqual
(
discussionMock
.
notes
.
length
);
discussion
.
diff_file
=
mockDiffFile
;
discussion
.
diff_discussion
=
true
;
const
diffDiscussionVm
=
new
Component
({
store
,
propsData
:
{
discussion
},
}).
$mount
();
expect
(
diffDiscussionVm
.
$el
.
querySelector
(
'
.discussion-header
'
)).
not
.
toBeNull
();
});
});
describe
(
'
actions
'
,
()
=>
{
describe
(
'
actions
'
,
()
=>
{
...
...
spec/javascripts/notes/components/toggle_replies_widget_spec.js
0 → 100644
View file @
cd5ddc4f
import
Vue
from
'
vue
'
;
import
toggleRepliesWidget
from
'
~/notes/components/toggle_replies_widget.vue
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
import
{
note
}
from
'
../mock_data
'
;
const
deepCloneObject
=
obj
=>
JSON
.
parse
(
JSON
.
stringify
(
obj
));
describe
(
'
toggle replies widget for notes
'
,
()
=>
{
let
vm
;
let
ToggleRepliesWidget
;
const
noteFromOtherUser
=
deepCloneObject
(
note
);
noteFromOtherUser
.
author
.
username
=
'
fatihacet
'
;
const
noteFromAnotherUser
=
deepCloneObject
(
note
);
noteFromAnotherUser
.
author
.
username
=
'
mgreiling
'
;
noteFromAnotherUser
.
author
.
name
=
'
Mike Greiling
'
;
const
replies
=
[
note
,
note
,
note
,
noteFromOtherUser
,
noteFromAnotherUser
];
beforeEach
(()
=>
{
ToggleRepliesWidget
=
Vue
.
extend
(
toggleRepliesWidget
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
});
describe
(
'
collapsed state
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
ToggleRepliesWidget
,
{
replies
,
collapsed
:
true
,
});
});
it
(
'
should render the collapsed
'
,
()
=>
{
const
vmTextContent
=
vm
.
$el
.
textContent
.
replace
(
/
\s\s
+/g
,
'
'
);
expect
(
vm
.
$el
.
classList
.
contains
(
'
collapsed
'
)).
toEqual
(
true
);
expect
(
vm
.
$el
.
querySelectorAll
(
'
.user-avatar-link
'
).
length
).
toEqual
(
3
);
expect
(
vm
.
$el
.
querySelector
(
'
time
'
)).
not
.
toBeNull
();
expect
(
vmTextContent
).
toContain
(
'
5 replies
'
);
expect
(
vmTextContent
).
toContain
(
`Last reply by
${
noteFromAnotherUser
.
author
.
name
}
`
);
});
it
(
'
should emit toggle event when the replies text clicked
'
,
()
=>
{
const
spy
=
spyOn
(
vm
,
'
$emit
'
);
vm
.
$el
.
querySelector
(
'
.js-replies-text
'
).
click
();
expect
(
spy
).
toHaveBeenCalledWith
(
'
toggle
'
);
});
});
describe
(
'
expanded state
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
ToggleRepliesWidget
,
{
replies
,
collapsed
:
false
,
});
});
it
(
'
should render expanded state
'
,
()
=>
{
const
vmTextContent
=
vm
.
$el
.
textContent
.
replace
(
/
\s\s
+/g
,
'
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.collapse-replies-btn
'
)).
not
.
toBeNull
();
expect
(
vmTextContent
).
toContain
(
'
Collapse replies
'
);
});
it
(
'
should emit toggle event when the collapse replies text called
'
,
()
=>
{
const
spy
=
spyOn
(
vm
,
'
$emit
'
);
vm
.
$el
.
querySelector
(
'
.js-collapse-replies
'
).
click
();
expect
(
spy
).
toHaveBeenCalledWith
(
'
toggle
'
);
});
});
});
spec/support/features/discussion_comments_shared_example.rb
View file @
cd5ddc4f
...
@@ -150,17 +150,25 @@ shared_examples 'discussion comments' do |resource_name|
...
@@ -150,17 +150,25 @@ shared_examples 'discussion comments' do |resource_name|
end
end
if
resource_name
==
'merge request'
if
resource_name
==
'merge request'
let
(
:note_id
)
{
find
(
"
#{
comments_selector
}
.note"
,
match: :first
)[
'data-note-id'
]
}
let
(
:note_id
)
{
find
(
"
#{
comments_selector
}
.note:first-child"
,
match: :first
)[
'data-note-id'
]
}
let
(
:reply_id
)
{
find
(
"
#{
comments_selector
}
.note:last-child"
,
match: :first
)[
'data-note-id'
]
}
it
'shows resolved discussion when toggled'
do
it
'shows resolved discussion when toggled'
do
find
(
"
#{
comments_selector
}
.js-vue-discussion-reply"
).
click
find
(
"
#{
comments_selector
}
.note-textarea"
).
send_keys
(
'a'
)
click_button
"Comment"
wait_for_requests
click_button
"Resolve discussion"
click_button
"Resolve discussion"
wait_for_requests
expect
(
page
).
to
have_selector
(
".note-row-
#{
note_id
}
"
,
visible:
true
)
expect
(
page
).
to
have_selector
(
".note-row-
#{
note_id
}
"
,
visible:
true
)
refresh
refresh
click_button
"
Toggle discussion
"
click_button
"
1 reply
"
expect
(
page
).
to
have_selector
(
".note-row-
#{
note
_id
}
"
,
visible:
true
)
expect
(
page
).
to
have_selector
(
".note-row-
#{
reply
_id
}
"
,
visible:
true
)
end
end
end
end
end
end
...
...
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