Commit 000087ab authored by 🤖 GitLab Bot 🤖's avatar 🤖 GitLab Bot 🤖
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 5de7e7c7
......@@ -13,11 +13,6 @@ import * as Emoji from '~/emoji';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
const requestAnimationFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.setTimeout;
const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; // For separating lists produced by ruby's Array#toSentence
......
......@@ -51,6 +51,7 @@ export default {
'scrollToDraft',
'toggleResolveDiscussion',
]),
...mapActions(['setSelectedCommentPositionHover']),
update(data) {
this.updateDraft(data);
},
......@@ -67,7 +68,11 @@ export default {
};
</script>
<template>
<article class="draft-note-component note-wrapper">
<article
class="draft-note-component note-wrapper"
@mouseenter="setSelectedCommentPositionHover(draft.position.line_range)"
@mouseleave="setSelectedCommentPositionHover()"
>
<ul class="notes draft-notes">
<noteable-note
:note="draft"
......
......@@ -148,10 +148,7 @@ export default {
<template>
<div class="content discussion-form discussion-form-container discussion-notes">
<div
v-if="glFeatures.multilineComments"
class="gl-mb-3 gl-text-gray-700 gl-border-gray-100 gl-border-b-solid gl-border-b-1 gl-pb-3"
>
<div v-if="glFeatures.multilineComments" class="gl-mb-3 gl-text-gray-700 gl-pb-3">
<multiline-comment-form
v-model="commentLineStart"
:line="line"
......
......@@ -37,6 +37,11 @@ export default {
required: false,
default: false,
},
isCommented: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
......@@ -47,7 +52,10 @@ export default {
...mapGetters('diffs', ['fileLineCoverage']),
...mapState({
isHighlighted(state) {
return this.line.line_code !== null && this.line.line_code === state.diffs.highlightedRow;
if (this.isCommented) return true;
const lineCode = this.line.line_code;
return lineCode ? lineCode === state.diffs.highlightedRow : false;
},
}),
isContextLine() {
......
<script>
import { mapGetters } from 'vuex';
import { mapGetters, mapState } from 'vuex';
import draftCommentsMixin from '~/diffs/mixins/draft_comments';
import InlineDraftCommentRow from '~/batch_comments/components/inline_draft_comment_row.vue';
import inlineDiffTableRow from './inline_diff_table_row.vue';
import inlineDiffCommentRow from './inline_diff_comment_row.vue';
import inlineDiffExpansionRow from './inline_diff_expansion_row.vue';
import { getCommentedLines } from '~/notes/components/multiline_comment_utils';
export default {
components: {
......@@ -31,9 +32,19 @@ export default {
},
computed: {
...mapGetters('diffs', ['commitId']),
...mapState({
selectedCommentPosition: ({ notes }) => notes.selectedCommentPosition,
selectedCommentPositionHover: ({ notes }) => notes.selectedCommentPositionHover,
}),
diffLinesLength() {
return this.diffLines.length;
},
commentedLines() {
return getCommentedLines(
this.selectedCommentPosition || this.selectedCommentPositionHover,
this.diffLines,
);
},
},
userColorScheme: window.gon.user_color_scheme,
};
......@@ -67,6 +78,7 @@ export default {
:file-path="diffFile.file_path"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
/>
<inline-diff-comment-row
:key="`icr-${line.line_code || index}`"
......
......@@ -40,6 +40,11 @@ export default {
required: false,
default: false,
},
isCommented: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
......@@ -51,6 +56,8 @@ export default {
...mapGetters('diffs', ['fileLineCoverage']),
...mapState({
isHighlighted(state) {
if (this.isCommented) return true;
const lineCode =
(this.line.left && this.line.left.line_code) ||
(this.line.right && this.line.right.line_code);
......
<script>
import { mapGetters } from 'vuex';
import { mapGetters, mapState } from 'vuex';
import draftCommentsMixin from '~/diffs/mixins/draft_comments';
import ParallelDraftCommentRow from '~/batch_comments/components/parallel_draft_comment_row.vue';
import parallelDiffTableRow from './parallel_diff_table_row.vue';
import parallelDiffCommentRow from './parallel_diff_comment_row.vue';
import parallelDiffExpansionRow from './parallel_diff_expansion_row.vue';
import { getCommentedLines } from '~/notes/components/multiline_comment_utils';
export default {
components: {
......@@ -31,9 +32,19 @@ export default {
},
computed: {
...mapGetters('diffs', ['commitId']),
...mapState({
selectedCommentPosition: ({ notes }) => notes.selectedCommentPosition,
selectedCommentPositionHover: ({ notes }) => notes.selectedCommentPositionHover,
}),
diffLinesLength() {
return this.diffLines.length;
},
commentedLines() {
return getCommentedLines(
this.selectedCommentPosition || this.selectedCommentPositionHover,
this.diffLines,
);
},
},
userColorScheme: window.gon.user_color_scheme,
};
......@@ -69,6 +80,7 @@ export default {
:file-path="diffFile.file_path"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
/>
<parallel-diff-comment-row
:key="`dcr-${line.line_code || index}`"
......
fragment User on User {
id
avatarUrl
name
username
webUrl
}
......@@ -74,7 +74,7 @@ export default {
},
},
methods: {
...mapActions(['toggleDiscussion']),
...mapActions(['toggleDiscussion', 'setSelectedCommentPositionHover']),
componentName(note) {
if (note.isPlaceholderNote) {
if (note.placeholderType === SYSTEM_NOTE) {
......@@ -99,7 +99,11 @@ export default {
<template>
<div class="discussion-notes">
<ul class="notes">
<ul
class="notes"
@mouseenter="setSelectedCommentPositionHover(discussion.position.line_range)"
@mouseleave="setSelectedCommentPositionHover()"
>
<template v-if="shouldGroupReplies">
<component
:is="componentName(firstNote)"
......
<script>
import { mapActions } from 'vuex';
import { GlFormSelect, GlSprintf } from '@gitlab/ui';
import { getSymbol, getLineClasses } from './multiline_comment_utils';
......@@ -39,8 +40,13 @@ export default {
old_line: line.old_line,
new_line: line.new_line,
};
this.highlightSelection();
},
destroyed() {
this.setSelectedCommentPosition();
},
methods: {
...mapActions(['setSelectedCommentPosition']),
getSymbol({ type }) {
return getSymbol(type);
},
......@@ -50,6 +56,16 @@ export default {
updateCommentLineStart(value) {
this.commentLineStart = value;
this.$emit('input', value);
this.highlightSelection();
},
highlightSelection() {
const { line_code, new_line, old_line, type } = this.line;
const updatedLineRange = {
start: { ...this.commentLineStart },
end: { line_code, new_line, old_line, type },
};
this.setSelectedCommentPosition(updatedLineRange);
},
},
};
......
......@@ -90,3 +90,22 @@ export function formatLineRange(start, end) {
end: extractProps(end),
};
}
export function getCommentedLines(selectedCommentPosition, diffLines) {
if (!selectedCommentPosition) {
// This structure simplifies the logic that consumes this result
// by keeping the returned shape the same and adjusting the bounds
// to something unreachable. This way our component logic stays:
// "if index between start and end"
return {
startLine: diffLines.length + 1,
endLine: diffLines.length + 1,
};
}
const { start, end } = selectedCommentPosition;
const startLine = diffLines.findIndex(l => l.line_code === start.line_code);
const endLine = diffLines.findIndex(l => l.line_code === end.line_code);
return { startLine, endLine };
}
......@@ -186,6 +186,7 @@ export default {
eventHub.$on('enterEditMode', ({ noteId }) => {
if (noteId === this.note.id) {
this.isEditing = true;
this.setSelectedCommentPositionHover();
this.scrollToNoteIfNeeded($(this.$el));
}
});
......@@ -205,9 +206,11 @@ export default {
'toggleResolveNote',
'scrollToNoteIfNeeded',
'updateAssignees',
'setSelectedCommentPositionHover',
]),
editHandler() {
this.isEditing = true;
this.setSelectedCommentPositionHover();
this.$emit('handleEdit');
},
deleteHandler() {
......@@ -284,6 +287,7 @@ export default {
} else {
this.isRequesting = false;
this.isEditing = true;
this.setSelectedCommentPositionHover();
this.$nextTick(() => {
const msg = __('Something went wrong while editing your comment. Please try again.');
Flash(msg, 'alert', this.$el);
......@@ -340,7 +344,7 @@ export default {
:line="line"
:comment-line-options="commentLineOptions"
:line-range="note.position.line_range"
class="gl-mb-3 gl-text-gray-700 gl-border-gray-100 gl-border-b-solid gl-border-b-1 gl-pb-3"
class="gl-mb-3 gl-text-gray-700 gl-pb-3"
/>
<div
v-else
......
......@@ -99,6 +99,14 @@ export const setDiscussionSortDirection = ({ commit }, direction) => {
commit(types.SET_DISCUSSIONS_SORT, direction);
};
export const setSelectedCommentPosition = ({ commit }, position) => {
commit(types.SET_SELECTED_COMMENT_POSITION, position);
};
export const setSelectedCommentPositionHover = ({ commit }, position) => {
commit(types.SET_SELECTED_COMMENT_POSITION_HOVER, position);
};
export const removeNote = ({ commit, dispatch, state }, note) => {
const discussion = state.discussions.find(({ id }) => id === note.discussion_id);
......
......@@ -12,6 +12,15 @@ export default () => ({
lastFetchedAt: null,
currentDiscussionId: null,
batchSuggestionsInfo: [],
/**
* selectedCommentPosition & selectedCommentPosition structures are the same as `position.line_range`:
* {
* start: { line_code: string, new_line: number, old_line:number, type: string },
* end: { line_code: string, new_line: number, old_line:number, type: string },
* }
*/
selectedCommentPosition: null,
selectedCommentPositionHover: null,
// View layer
isToggleStateButtonLoading: false,
......
......@@ -33,6 +33,8 @@ export const SET_EXPAND_DISCUSSIONS = 'SET_EXPAND_DISCUSSIONS';
export const UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS = 'UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS';
export const SET_CURRENT_DISCUSSION_ID = 'SET_CURRENT_DISCUSSION_ID';
export const SET_DISCUSSIONS_SORT = 'SET_DISCUSSIONS_SORT';
export const SET_SELECTED_COMMENT_POSITION = 'SET_SELECTED_COMMENT_POSITION';
export const SET_SELECTED_COMMENT_POSITION_HOVER = 'SET_SELECTED_COMMENT_POSITION_HOVER';
// Issue
export const CLOSE_ISSUE = 'CLOSE_ISSUE';
......
......@@ -308,6 +308,14 @@ export default {
state.discussionSortOrder = sort;
},
[types.SET_SELECTED_COMMENT_POSITION](state, position) {
state.selectedCommentPosition = position;
},
[types.SET_SELECTED_COMMENT_POSITION_HOVER](state, position) {
state.selectedCommentPositionHover = position;
},
[types.DISABLE_COMMENTS](state, value) {
state.commentsDisabled = value;
},
......
......@@ -97,6 +97,7 @@ export default {
:path="entry.flatPath"
:type="entry.type"
:url="entry.webUrl"
:mode="entry.mode"
:submodule-tree-url="entry.treeUrl"
:lfs-oid="entry.lfsOid"
:loading-path="loadingPath"
......
......@@ -66,6 +66,11 @@ export default {
type: String,
required: true,
},
mode: {
type: String,
required: false,
default: '',
},
type: {
type: String,
required: true,
......@@ -140,6 +145,7 @@ export default {
>
<file-icon
:file-name="fullPath"
:file-mode="mode"
:folder="isFolder"
:submodule="isSubmodule"
:loading="path === loadingPath"
......
<script>
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
import getIconForFile from './file_icon/file_icon_map';
import { FILE_SYMLINK_MODE } from '../constants';
/* This is a re-usable vue component for rendering a svg sprite
icon
......@@ -24,6 +25,11 @@ export default {
type: String,
required: true,
},
fileMode: {
type: String,
required: false,
default: '',
},
folder: {
type: Boolean,
......@@ -60,8 +66,12 @@ export default {
},
},
computed: {
isSymlink() {
return this.fileMode === FILE_SYMLINK_MODE;
},
spriteHref() {
const iconName = this.submodule ? 'folder-git' : getIconForFile(this.fileName) || 'file';
return `${gon.sprite_file_icons}#${iconName}`;
},
folderIconName() {
......@@ -75,13 +85,11 @@ export default {
</script>
<template>
<span>
<svg v-if="!loading && !folder" :class="[iconSizeClass, cssClasses]">
<use v-bind="{ 'xlink:href': spriteHref }" /></svg
><gl-icon
v-if="!loading && folder"
:name="folderIconName"
:size="size"
class="folder-icon"
/><gl-loading-icon v-if="loading" :inline="true" />
<gl-loading-icon v-if="loading" :inline="true" />
<gl-icon v-else-if="isSymlink" name="symlink" :size="size" />
<svg v-else-if="!folder" :class="[iconSizeClass, cssClasses]">
<use v-bind="{ 'xlink:href': spriteHref }" />
</svg>
<gl-icon v-else :name="folderIconName" :size="size" class="folder-icon" />
</span>
</template>
......@@ -6,6 +6,8 @@ const INTERVALS = {
day: 'day',
};
export const FILE_SYMLINK_MODE = '120000';
export const timeRanges = [
{
label: __('30 minutes'),
......
......@@ -10,7 +10,7 @@
}
.design-pin {
transition: opacity 0.5s ease;
transition: opacity $gl-transition-duration-medium $general-hover-transition-curve;
&.inactive {
@include gl-opacity-5;
......@@ -108,6 +108,7 @@
.design-note {
padding: $gl-padding;
list-style: none;
transition: background $gl-transition-duration-medium $general-hover-transition-curve;
a {
color: inherit;
......@@ -150,7 +151,7 @@
}
.design-dropzone-card {
transition: border $general-hover-transition-duration $general-hover-transition-curve;
transition: border $gl-transition-duration-medium $general-hover-transition-curve;
color: $gl-text-color;
&:focus,
......
......@@ -92,3 +92,5 @@ def non_stable_cursor_sort?(sort)
end
end
end
Resolvers::IssuesResolver.prepend_if_ee('::EE::Resolvers::IssuesResolver')
# frozen_string_literal: true
module Types
# rubocop: disable Graphql/AuthorizeTypes
class IssueConnectionType < GraphQL::Types::Relay::BaseConnection
field :count, Integer, null: false,
description: 'Total count of collection'
def count
object.items.size
end
end
end
......@@ -4,6 +4,8 @@ module Types
class IssueType < BaseObject
graphql_name 'Issue'
connection_type_class(Types::IssueConnectionType)
implements(Types::Notes::NoteableType)
authorize :read_issue
......
- type = local_assigns.fetch(:type)
- bulk_issue_health_status_flag = Feature.enabled?(:bulk_update_health_status, @project&.group) && type == :issues && @project&.group&.feature_available?(:issuable_health_status)
- bulk_issue_health_status_flag = Feature.enabled?(:bulk_update_health_status, @project&.group, default_enabled: true) && type == :issues && @project&.group&.feature_available?(:issuable_health_status)
- epic_bulk_edit_flag = @project&.group&.feature_available?(:epics) && type == :issues
%aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } }
......
---
title: Improve animations of design note selection in design management
merge_request: 36927
author:
type: changed
---
title: Highlight commented rows
merge_request: 34432
author:
type: added
---
title: Show symlink icon in repository browser
merge_request: 36524
author:
type: fixed
......@@ -10,4 +10,7 @@ link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#alert
level: error
scope: raw
raw:
- 'NOTE: \*\*[^:]*\*\*'
- '((NOTE|TIP|CAUTION|DANGER): \*\*[^:]*\*\*)|'
- '((NOTE: \*\*NOTE:\*\*)|(TIP: \*\*TIP:\*\*)|(CAUTION: \*\*CAUTION:\*\*)|(DANGER: \*\*DANGER:\*\*))|'
- '((NOTE: \*\*note:\*\*)|(TIP: \*\*tip:\*\*)|(CAUTION: \*\*caution:\*\*)|(DANGER: \*\*danger:\*\*))|'
- '((NOTE|TIP|CAUTION|DANGER): \*\*.*\*\*.+)'
......@@ -62,7 +62,8 @@ JWT will provide you with a secret key for you to use.
}
```
NOTE: **Note:** For more information on each configuration option refer to
NOTE: **Note:**
For more information on each configuration option refer to
the [OmniAuth JWT usage documentation](https://github.com/mbleigh/omniauth-jwt#usage).
1. Change `YOUR_APP_SECRET` to the client secret and set `auth_url` to your redirect URL.
......