Commit 6162d5b0 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab-ce master

parents 83682e40 70a717da
......@@ -351,7 +351,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.7.0'
gem 'factory_bot_rails', '~> 4.8.2'
gem 'rspec-rails', '~> 3.7.0'
gem 'rspec-retry', '~> 0.4.5'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_profiling', '~> 0.0.5'
gem 'rspec-set', '~> 0.1.3'
gem 'rspec-parameterized', require: false
......
......@@ -829,8 +829,8 @@ GEM
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-support (~> 3.7.0)
rspec-retry (0.4.5)
rspec-core
rspec-retry (0.6.1)
rspec-core (> 3.3)
rspec-set (0.1.3)
rspec-support (3.7.1)
rspec_junit_formatter (0.4.1)
......@@ -1241,7 +1241,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 3.7.0)
rspec-retry (~> 0.4.5)
rspec-retry (~> 0.6.1)
rspec-set (~> 0.1.3)
rspec_junit_formatter
rspec_profiling (~> 0.0.5)
......
......@@ -8,6 +8,7 @@ import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_d
import NoPreviewViewer from '~/vue_shared/components/diff_viewer/viewers/no_preview.vue';
import InlineDiffView from './inline_diff_view.vue';
import ParallelDiffView from './parallel_diff_view.vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import NoteForm from '../../notes/components/note_form.vue';
import ImageDiffOverlay from './image_diff_overlay.vue';
import DiffDiscussions from './diff_discussions.vue';
......@@ -26,6 +27,7 @@ export default {
ImageDiffOverlay,
NotDiffableViewer,
NoPreviewViewer,
userAvatarLink,
DiffFileDrafts: () => import('ee_component/batch_comments/components/diff_file_drafts.vue'),
},
mixins: [diffLineNoteFormMixin, draftCommentsMixin],
......@@ -47,7 +49,7 @@ export default {
}),
...mapGetters('diffs', ['isInlineView', 'isParallelView']),
...mapGetters('diffs', ['getCommentFormForDiffFile']),
...mapGetters(['getNoteableData', 'noteableType']),
...mapGetters(['getNoteableData', 'noteableType', 'getUserData']),
diffMode() {
return getDiffMode(this.diffFile);
},
......@@ -72,6 +74,9 @@ export default {
diffFileHash() {
return this.diffFile.file_hash;
},
author() {
return this.getUserData;
},
},
methods: {
...mapActions('diffs', ['saveDiffDiscussion', 'closeDiffFileCommentForm']),
......@@ -134,6 +139,14 @@ export default {
:can-comment="getNoteableData.current_user.can_create_note"
/>
<div v-if="showNotesContainer" class="note-container">
<user-avatar-link
v-if="diffFileCommentForm && author"
:link-href="author.path"
:img-src="author.avatar_url"
:img-alt="author.name"
:img-size="40"
class="d-none d-sm-block new-comment"
/>
<diff-discussions
v-if="diffFile.discussions.length"
class="diff-file-discussions"
......
......@@ -4,11 +4,13 @@ import { s__ } from '~/locale';
import diffLineNoteFormMixin from 'ee_else_ce/notes/mixins/diff_line_note_form';
import noteForm from '../../notes/components/note_form.vue';
import autosave from '../../notes/mixins/autosave';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import { DIFF_NOTE_TYPE } from '../constants';
export default {
components: {
noteForm,
userAvatarLink,
},
mixins: [autosave, diffLineNoteFormMixin],
props: {
......@@ -41,7 +43,16 @@ export default {
diffViewType: state => state.diffs.diffViewType,
}),
...mapGetters('diffs', ['getDiffFileByHash']),
...mapGetters(['isLoggedIn', 'noteableType', 'getNoteableData', 'getNotesDataByProp']),
...mapGetters([
'isLoggedIn',
'noteableType',
'getNoteableData',
'getNotesDataByProp',
'getUserData',
]),
author() {
return this.getUserData;
},
formData() {
return {
noteableData: this.noteableData,
......@@ -99,6 +110,14 @@ export default {
<template>
<div class="content discussion-form discussion-form-container discussion-notes">
<user-avatar-link
v-if="author"
:link-href="author.path"
:img-src="author.avatar_url"
:img-alt="author.name"
:img-size="40"
class="d-none d-sm-block"
/>
<note-form
ref="noteForm"
:is-editing="true"
......
......@@ -990,6 +990,14 @@ export default class Notes {
form.find('#note_position').val(dataHolder.attr('data-position'));
form
.prepend(
`<div class="avatar-note-form-holder"><div class="content"><a href="${escape(
gon.current_username,
)}" class="user-avatar-link d-none d-sm-block"><img class="avatar s40" src="${encodeURI(
gon.current_user_avatar_url,
)}" alt="${escape(gon.current_user_fullname)}" /></a></div></div>`,
)
.append('</div>')
.find('.js-close-discussion-note-form')
.show()
.removeClass('hide');
......@@ -1025,6 +1033,9 @@ export default class Notes {
target: $link,
lineType: link.dataset.lineType,
showReplyInput,
currentUsername: gon.current_username,
currentUserAvatar: gon.current_user_avatar_url,
currentUserFullname: gon.current_user_fullname,
});
}
......@@ -1053,7 +1064,15 @@ export default class Notes {
this.setupDiscussionNoteForm($link, newForm);
}
toggleDiffNote({ target, lineType, forceShow, showReplyInput = false }) {
toggleDiffNote({
target,
lineType,
forceShow,
showReplyInput = false,
currentUsername,
currentUserAvatar,
currentUserFullname,
}) {
var $link,
addForm,
hasNotes,
......@@ -1546,7 +1565,9 @@ export default class Notes {
<div class="note-header">
<div class="note-header-info">
<a href="/${_.escape(currentUsername)}">
<span class="d-none d-sm-inline-block">${_.escape(currentUsername)}</span>
<span class="d-none d-sm-inline-block bold">${_.escape(
currentUsername,
)}</span>
<span class="note-headline-light">${_.escape(currentUsername)}</span>
</a>
</div>
......
......@@ -82,7 +82,7 @@ export default {
:data-username="author.username"
>
<slot name="note-header-info"></slot>
<span class="note-header-author-name">{{ author.name }}</span>
<span class="note-header-author-name bold">{{ author.name }}</span>
<span v-if="author.status_tooltip_html" v-html="author.status_tooltip_html"></span>
<span class="note-headline-light">@{{ author.username }}</span>
</a>
......
......@@ -87,7 +87,11 @@ export default {
'unresolvedDiscussionsCount',
'hasUnresolvedDiscussions',
'showJumpToNextDiscussion',
'getUserData',
]),
currentUser() {
return this.getUserData;
},
author() {
return this.firstNote.author;
},
......@@ -377,6 +381,14 @@ Please check your network connection and try again.`;
:class="{ 'is-replying': isReplying }"
class="discussion-reply-holder"
>
<user-avatar-link
v-if="!isReplying && currentUser"
:link-href="currentUser.path"
:img-src="currentUser.avatar_url"
:img-alt="currentUser.name"
:img-size="40"
class="d-none d-sm-block"
/>
<discussion-actions
v-if="!isReplying && userCanReply"
:discussion="discussion"
......@@ -388,18 +400,27 @@ Please check your network connection and try again.`;
@resolve="resolveHandler"
@jumpToNextDiscussion="jumpToNextDiscussion"
/>
<note-form
v-if="isReplying"
ref="noteForm"
:discussion="discussion"
:is-editing="false"
:line="diffLine"
save-button-title="Comment"
:autosave-key="autosaveKey"
@handleFormUpdateAddToReview="addReplyToReview"
@handleFormUpdate="saveReply"
@cancelForm="cancelReplyForm"
/>
<div v-if="isReplying" class="avatar-note-form-holder">
<user-avatar-link
v-if="currentUser"
:link-href="currentUser.path"
:img-src="currentUser.avatar_url"
:img-alt="currentUser.name"
:img-size="40"
class="d-none d-sm-block"
/>
<note-form
ref="noteForm"
:discussion="discussion"
:is-editing="false"
:line="diffLine"
save-button-title="Comment"
:autosave-key="autosaveKey"
@handleFormUpdateAddToReview="addReplyToReview"
@handleFormUpdate="saveReply"
@cancelForm="cancelReplyForm"
/>
</div>
<note-signed-out-widget v-if="!userCanReply" />
</div>
</template>
......
......@@ -51,7 +51,7 @@ export default {
<div class="note-header">
<div class="note-header-info">
<a :href="getUserData.path">
<span class="d-none d-sm-inline-block">{{ getUserData.name }}</span>
<span class="d-none d-sm-inline-block bold">{{ getUserData.name }}</span>
<span class="note-headline-light">@{{ getUserData.username }}</span>
</a>
</div>
......
......@@ -249,7 +249,7 @@
padding: 6px 16px;
border-color: $border-color;
color: $gray-darkest;
background-color: $gray-light;
background-color: $white-light;
&:hover,
&:active,
......@@ -258,7 +258,6 @@
box-shadow: none;
border-color: lighten($blue-300, 20%);
color: $gray-darkest;
background-color: $gray-light;
}
}
......
......@@ -453,6 +453,28 @@ span.idiff {
}
}
.note-container {
.user-avatar-link.new-comment {
position: absolute;
margin: 40px $gl-padding 0 116px;
~ .note-edit-form form.edit-note {
@include media-breakpoint-up(sm) {
margin-left: $note-icon-gutter-width;
}
}
}
}
.diff-discussions:not(:last-child) .discussion .discussion-body {
padding-bottom: $gl-padding;
.discussion-reply-holder {
border-bottom: 1px solid $gray-100;
border-radius: 0;
}
}
.md-previewer {
padding: $gl-padding;
}
......
......@@ -42,8 +42,8 @@
}
}
.avatar {
margin-right: 15px;
img.avatar {
margin-right: $gl-padding;
}
.controls {
......
......@@ -1008,6 +1008,10 @@ table.code {
display: block;
}
}
.note-edit-form {
margin-left: $note-icon-gutter-width;
}
}
.discussion-body .image .frame {
......
......@@ -258,8 +258,15 @@ ul.related-merge-requests > li {
}
}
.discussion-reply-holder .note-edit-form {
display: block;
.discussion-reply-holder {
.avatar-note-form-holder .note-edit-form {
display: block;
margin-left: $note-icon-gutter-width;
@include media-breakpoint-down(xs) {
margin-left: 0;
}
}
}
.issue-sort-dropdown {
......
......@@ -59,6 +59,7 @@
border-radius: $border-radius-base;
transition: border-color ease-in-out 0.15s,
box-shadow ease-in-out 0.15s;
background-color: $white-light;
&.is-focused {
@extend .form-control:focus;
......@@ -173,6 +174,16 @@
.discussion-form {
background-color: $white-light;
@include media-breakpoint-down(xs) {
.user-avatar-link {
display: none;
}
.note-edit-form {
margin-left: 0;
}
}
}
table {
......@@ -239,13 +250,25 @@ table {
.diff-file,
.commit-diff {
.discussion-reply-holder {
background-color: $white-light;
background-color: $gray-light;
border-radius: 0 0 3px 3px;
padding: $gl-padding;
border-top: 1px solid $gray-100;
+ .new-note {
background-color: $gray-light;
border-top: 1px solid $gray-100;
}
&.is-replying {
padding-bottom: $gl-padding;
}
.user-avatar-link {
img {
margin-top: -3px;
}
}
}
}
......
......@@ -80,21 +80,17 @@ $note-form-margin-left: 72px;
}
}
li.note {
border-bottom: 1px solid $border-color;
}
.replies-toggle {
background-color: $gray-light;
padding: $gl-padding-8 $gl-padding;
border-top: 1px solid $gray-100;
border-bottom: 1px solid $gray-100;
.collapse-replies-btn:hover {
color: $blue-600;
}
&.expanded {
border-bottom: 1px solid $border-color;
span {
cursor: pointer;
}
......@@ -211,8 +207,13 @@ $note-form-margin-left: 72px;
display: none;
}
.user-avatar-link img {
margin-top: $gl-padding-8;
}
.note-edit-form {
display: block;
margin-left: 0;
&.current-note-edit-form + .note-awards {
display: none;
......@@ -519,12 +520,30 @@ $note-form-margin-left: 72px;
}
}
.commit-diff {
.notes-content {
background-color: $white-light;
.code-commit .notes-content,
.diff-viewer > .image ~ .note-container {
background-color: $white-light;
.avatar-note-form-holder {
.user-avatar-link img {
margin: 13px $gl-padding $gl-padding;
}
form,
~ .discussion-form-container {
padding: $gl-padding;
@include media-breakpoint-up(sm) {
margin-left: $note-icon-gutter-width;
}
}
}
}
.diff-viewer > .image ~ .note-container form.new-note {
margin-left: 0;
}
.discussion-header,
.note-header-info {
a {
......
......@@ -19,20 +19,24 @@
.discussion-reply-holder
- if can_create_note?
%a.user-avatar-link.d-none.d-sm-block{ href: user_path(current_user) }
= image_tag avatar_icon_for_user(current_user), alt: current_user.to_reference, class: 'avatar s40'
- if discussion.potentially_resolvable?
- line_type = local_assigns.fetch(:line_type, nil)
.btn-group.discussion-with-resolve-btn{ role: "group" }
.btn-group{ role: "group" }
= link_to_reply_discussion(discussion, line_type)
.discussion-with-resolve-btn
.btn-group.discussion-with-resolve-btn{ role: "group" }
.btn-group{ role: "group" }
= link_to_reply_discussion(discussion, line_type)
= render "discussions/resolve_all", discussion: discussion
= render "discussions/resolve_all", discussion: discussion
.btn-group.discussion-actions
= render "discussions/new_issue_for_discussion", discussion: discussion, merge_request: discussion.noteable
= render "discussions/jump_to_next", discussion: discussion
.btn-group.discussion-actions
= render "discussions/new_issue_for_discussion", discussion: discussion, merge_request: discussion.noteable
= render "discussions/jump_to_next", discussion: discussion
- else
= link_to_reply_discussion(discussion)
.discussion-with-resolve-btn
= link_to_reply_discussion(discussion)
- elsif !current_user
.disabled-comment.text-center
Please
......
/ Side-by-side diff view
.text-file{ data: diff_view_data }
%table.diff-wrap-lines.code.js-syntax-highlight
%table.diff-wrap-lines.code.code-commit.js-syntax-highlight
- diff_file.parallel_diff_lines.each do |line|
- left = line[:left]
- right = line[:right]
......
......@@ -3,7 +3,7 @@
.suppressed-container
%a.show-suppressed-diff.cursor-pointer.js-show-suppressed-diff= _("Changes suppressed. Click to show.")
%table.text-file.diff-wrap-lines.code.js-syntax-highlight.commit-diff{ data: diff_view_data, class: too_big ? 'hide' : '' }
%table.text-file.diff-wrap-lines.code.code-commit.js-syntax-highlight.commit-diff{ data: diff_view_data, class: too_big ? 'hide' : '' }
= render partial: "projects/diffs/line",
collection: diff_file.highlighted_diff_lines,
as: :line,
......
%inline-conflict-lines{ "inline-template" => "true", ":file" => "file" }
%table.diff-wrap-lines.code.js-syntax-highlight
%table.diff-wrap-lines.code.code-commit.js-syntax-highlight
%tr.line_holder.diff-inline{ "v-for" => "line in file.inlineLines" }
%td.diff-line-num.new_line{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader" }
%a {{line.new_line}}
......
......@@ -31,7 +31,7 @@
.note-header
.note-header-info
%a{ href: user_path(note.author) }
%span.note-header-author-name
%span.note-header-author-name.bold
= sanitize(note.author.name)
= user_status(note.author)
%span.note-headline-light
......
---
title: Resolve Snippet icon button is misaligned
merge_request: 28522
author: Marcel van Remmerden
author:
type: other
---
title: Give New Snippet button green outline
merge_request: 28559
author: Marcel van Remmerden
author:
type: other
---
title: Add hover and focus to Attach a file
merge_request: 28682
author: Marcel van Remmerden
author:
type: fixed
---
title: Change collapse icon size to size of profile picture
title: Change collapse icon size to size of profile picture
merge_request: 28512
author: Marcel van Remmerden
author:
type: other
---
title: Implement borderless discussion design with new reply field
merge_request: 28580
author:
type: added
---
title: Include the port in the URLs of the API Link headers
merge_request: 29267
author:
type: fixed
......@@ -23,7 +23,7 @@ module API
def base_request_uri
@base_request_uri ||= URI.parse(request.url).tap do |uri|
uri.host = Gitlab.config.gitlab.host
uri.port = nil
uri.port = Gitlab.config.gitlab.port
end
end
......
......@@ -2,8 +2,12 @@ require 'spec_helper'
describe API::Helpers::Pagination do
let(:resource) { Project.all }
let(:incoming_api_projects_url) { "#{Gitlab.config.gitlab.url}:8080/api/v4/projects" }
let(:canonical_api_projects_url) { "#{Gitlab.config.gitlab.url}/api/v4/projects" }
let(:custom_port) { 8080 }
let(:incoming_api_projects_url) { "#{Gitlab.config.gitlab.url}:#{custom_port}/api/v4/projects" }
before do
stub_config_setting(port: custom_port)
end
subject do
Class.new.include(described_class).new
......@@ -48,7 +52,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[1].id).to_query}")
expect_header('X-Next-Page', "#{incoming_api_projects_url}?#{query.merge(ks_prev_id: projects[1].id).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
......@@ -71,7 +75,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[2].id).to_query}")
expect_header('X-Next-Page', "#{incoming_api_projects_url}?#{query.merge(ks_prev_id: projects[2].id).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
......@@ -171,7 +175,7 @@ describe API::Helpers::Pagination do
it 'returns the right link to the next page' do
expect_header('X-Per-Page', '2')
expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name).to_query}")
expect_header('X-Next-Page', "#{incoming_api_projects_url}?#{query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
end
......@@ -224,9 +228,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).not_to include('rel="prev"')
end
......@@ -290,8 +294,8 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).not_to include('rel="last"')
expect(val).not_to include('rel="prev"')
end
......@@ -318,9 +322,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '1')
expect_header('Link', anything) do |_key, val|
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="prev"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="prev"))
expect(val).not_to include('rel="next"')
end
......@@ -367,8 +371,8 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="last"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="last"))
expect(val).not_to include('rel="prev"')
expect(val).not_to include('rel="next"')
expect(val).not_to include('page=0')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment