Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-26 12:07:52 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-26 12:07:52 +0300
commit7e019504f5ac6decde690565857238e7e59aa034 (patch)
treefab8832b40e25fc9bc1ae54b9303b95ea146b5d5 /app/assets/javascripts
parent116d4e56e83a1f408afe710ce070e699ba206475 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue16
-rw-r--r--app/assets/javascripts/monitoring/components/panel_type.vue5
-rw-r--r--app/assets/javascripts/mr_notes/index.js2
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue93
-rw-r--r--app/assets/javascripts/notes/components/sort_discussion.vue64
-rw-r--r--app/assets/javascripts/notes/constants.js2
-rw-r--r--app/assets/javascripts/notes/index.js2
-rw-r--r--app/assets/javascripts/notes/sort_discussions.js16
-rw-r--r--app/assets/javascripts/notes/stores/actions.js4
-rw-r--r--app/assets/javascripts/notes/stores/getters.js19
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js2
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/ordered_layout.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue8
17 files changed, 214 insertions, 44 deletions
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index dbfb3e97c20..531d23bb6e5 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -19,7 +19,12 @@ import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { s__ } from '~/locale';
import createFlash from '~/flash';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import { mergeUrlParams, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
+import {
+ mergeUrlParams,
+ redirectTo,
+ refreshCurrentPage,
+ updateHistory,
+} from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url';
import Icon from '~/vue_shared/components/icon.vue';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
@@ -356,6 +361,14 @@ export default {
refreshDashboard() {
refreshCurrentPage();
},
+
+ onTimeRangeZoom({ start, end }) {
+ updateHistory({
+ url: mergeUrlParams({ start, end }, window.location.href),
+ title: document.title,
+ });
+ this.selectedTimeRange = { start, end };
+ },
},
addMetric: {
title: s__('Metrics|Add metric'),
@@ -577,6 +590,7 @@ export default {
:alerts-endpoint="alertsEndpoint"
:prometheus-alerts-available="prometheusAlertsAvailable"
:index="`${index}-${graphIndex}`"
+ @timerangezoom="onTimeRangeZoom"
/>
</div>
</div>
diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue
index ba92b72a71d..79f2c8ad41f 100644
--- a/app/assets/javascripts/monitoring/components/panel_type.vue
+++ b/app/assets/javascripts/monitoring/components/panel_type.vue
@@ -23,6 +23,10 @@ import MonitorEmptyChart from './charts/empty_chart.vue';
import TrackEventDirective from '~/vue_shared/directives/track_event';
import { timeRangeToUrl, downloadCSVOptions, generateLinkToChartOptions } from '../utils';
+const events = {
+ timeRangeZoom: 'timerangezoom',
+};
+
export default {
components: {
MonitorSingleStatChart,
@@ -159,6 +163,7 @@ export default {
},
onDatazoom({ start, end }) {
this.zoomedTimeRange = { start, end };
+ this.$emit(events.timeRangeZoom, { start, end });
},
},
};
diff --git a/app/assets/javascripts/mr_notes/index.js b/app/assets/javascripts/mr_notes/index.js
index 8eccba07c38..c1edf7be870 100644
--- a/app/assets/javascripts/mr_notes/index.js
+++ b/app/assets/javascripts/mr_notes/index.js
@@ -4,6 +4,7 @@ import initNotesApp from './init_notes';
import initDiffsApp from '../diffs';
import discussionCounter from '../notes/components/discussion_counter.vue';
import initDiscussionFilters from '../notes/discussion_filters';
+import initSortDiscussions from '../notes/sort_discussions';
import MergeRequest from '../merge_request';
import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
@@ -32,5 +33,6 @@ export default function initMrNotes() {
});
initDiscussionFilters(store);
+ initSortDiscussions(store);
initDiffsApp(store);
}
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 762228dd138..c1dd56aedf2 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -12,6 +12,7 @@ import commentForm from './comment_form.vue';
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
import skeletonLoadingContainer from '../../vue_shared/components/notes/skeleton_note.vue';
+import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
import { __ } from '~/locale';
import initUserPopovers from '~/user_popovers';
@@ -27,6 +28,7 @@ export default {
placeholderSystemNote,
skeletonLoadingContainer,
discussionFilterNote,
+ OrderedLayout,
},
props: {
noteableData: {
@@ -70,7 +72,11 @@ export default {
'getNoteableData',
'userCanReply',
'discussionTabCounter',
+ 'sortDirection',
]),
+ sortDirDesc() {
+ return this.sortDirection === constants.DESC;
+ },
discussionTabCounterText() {
return this.isLoading ? '' : this.discussionTabCounter;
},
@@ -91,6 +97,9 @@ export default {
canReply() {
return this.userCanReply && !this.commentsDisabled;
},
+ slotKeys() {
+ return this.sortDirDesc ? ['form', 'comments'] : ['comments', 'form'];
+ },
},
watch: {
shouldShow() {
@@ -156,6 +165,9 @@ export default {
'convertToDiscussion',
'stopPolling',
]),
+ discussionIsIndividualNoteAndNotConverted(discussion) {
+ return discussion.individual_note && !this.convertedDisscussionIds.includes(discussion.id);
+ },
handleHashChanged() {
const noteId = this.checkLocationHash();
@@ -232,44 +244,51 @@ export default {
<template>
<div v-show="shouldShow" id="notes">
- <ul id="notes-list" class="notes main-notes-list timeline">
- <template v-for="discussion in allDiscussions">
- <skeleton-loading-container v-if="discussion.isSkeletonNote" :key="discussion.id" />
- <template v-else-if="discussion.isPlaceholderNote">
- <placeholder-system-note
- v-if="discussion.placeholderType === $options.systemNote"
- :key="discussion.id"
- :note="discussion.notes[0]"
- />
- <placeholder-note v-else :key="discussion.id" :note="discussion.notes[0]" />
- </template>
- <template
- v-else-if="discussion.individual_note && !convertedDisscussionIds.includes(discussion.id)"
- >
- <system-note
- v-if="discussion.notes[0].system"
- :key="discussion.id"
- :note="discussion.notes[0]"
- />
- <noteable-note
- v-else
- :key="discussion.id"
- :note="discussion.notes[0]"
- :show-reply-button="canReply"
- @startReplying="startReplying(discussion.id)"
- />
- </template>
- <noteable-discussion
- v-else
- :key="discussion.id"
- :discussion="discussion"
- :render-diff-file="true"
- :help-page-path="helpPagePath"
+ <ordered-layout :slot-keys="slotKeys">
+ <template #form>
+ <comment-form
+ v-if="!commentsDisabled"
+ class="js-comment-form"
+ :noteable-type="noteableType"
/>
</template>
- <discussion-filter-note v-show="commentsDisabled" />
- </ul>
-
- <comment-form v-if="!commentsDisabled" :noteable-type="noteableType" />
+ <template #comments>
+ <ul id="notes-list" class="notes main-notes-list timeline">
+ <template v-for="discussion in allDiscussions">
+ <skeleton-loading-container v-if="discussion.isSkeletonNote" :key="discussion.id" />
+ <template v-else-if="discussion.isPlaceholderNote">
+ <placeholder-system-note
+ v-if="discussion.placeholderType === $options.systemNote"
+ :key="discussion.id"
+ :note="discussion.notes[0]"
+ />
+ <placeholder-note v-else :key="discussion.id" :note="discussion.notes[0]" />
+ </template>
+ <template v-else-if="discussionIsIndividualNoteAndNotConverted(discussion)">
+ <system-note
+ v-if="discussion.notes[0].system"
+ :key="discussion.id"
+ :note="discussion.notes[0]"
+ />
+ <noteable-note
+ v-else
+ :key="discussion.id"
+ :note="discussion.notes[0]"
+ :show-reply-button="canReply"
+ @startReplying="startReplying(discussion.id)"
+ />
+ </template>
+ <noteable-discussion
+ v-else
+ :key="discussion.id"
+ :discussion="discussion"
+ :render-diff-file="true"
+ :help-page-path="helpPagePath"
+ />
+ </template>
+ <discussion-filter-note v-show="commentsDisabled" />
+ </ul>
+ </template>
+ </ordered-layout>
</div>
</template>
diff --git a/app/assets/javascripts/notes/components/sort_discussion.vue b/app/assets/javascripts/notes/components/sort_discussion.vue
new file mode 100644
index 00000000000..16eded52763
--- /dev/null
+++ b/app/assets/javascripts/notes/components/sort_discussion.vue
@@ -0,0 +1,64 @@
+<script>
+import { GlIcon } from '@gitlab/ui';
+import { mapActions, mapGetters } from 'vuex';
+import { __ } from '~/locale';
+import { ASC, DESC } from '../constants';
+
+const SORT_OPTIONS = [
+ { key: DESC, text: __('Newest first'), cls: 'js-newest-first' },
+ { key: ASC, text: __('Oldest first'), cls: 'js-oldest-first' },
+];
+
+export default {
+ SORT_OPTIONS,
+ components: {
+ GlIcon,
+ },
+ computed: {
+ ...mapGetters(['sortDirection']),
+ selectedOption() {
+ return SORT_OPTIONS.find(({ key }) => this.sortDirection === key);
+ },
+ dropdownText() {
+ return this.selectedOption.text;
+ },
+ },
+ methods: {
+ ...mapActions(['setDiscussionSortDirection']),
+ fetchSortedDiscussions(direction) {
+ if (this.isDropdownItemActive(direction)) {
+ return;
+ }
+
+ this.setDiscussionSortDirection(direction);
+ },
+ isDropdownItemActive(sortDir) {
+ return sortDir === this.sortDirection;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="mr-2 d-inline-block align-bottom full-width-mobile">
+ <button class="btn btn-sm js-dropdown-text" data-toggle="dropdown" aria-expanded="false">
+ {{ dropdownText }}
+ <gl-icon name="chevron-down" />
+ </button>
+ <div ref="dropdownMenu" class="dropdown-menu dropdown-menu-selectable dropdown-menu-right">
+ <div class="dropdown-content">
+ <ul>
+ <li v-for="{ text, key, cls } in $options.SORT_OPTIONS" :key="key">
+ <button
+ :class="[cls, { 'is-active': isDropdownItemActive(key) }]"
+ type="button"
+ @click="fetchSortedDiscussions(key)"
+ >
+ {{ text }}
+ </button>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index e9a81bc9553..c1449237f8a 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -19,6 +19,8 @@ export const DISCUSSION_FILTERS_DEFAULT_VALUE = 0;
export const DISCUSSION_TAB_LABEL = 'show';
export const NOTE_UNDERSCORE = 'note_';
export const TIME_DIFFERENCE_VALUE = 10;
+export const ASC = 'asc';
+export const DESC = 'desc';
export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE,
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 30372103590..8f9e2359e0d 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import notesApp from './components/notes_app.vue';
import initDiscussionFilters from './discussion_filters';
+import initSortDiscussions from './sort_discussions';
import createStore from './stores';
document.addEventListener('DOMContentLoaded', () => {
@@ -50,4 +51,5 @@ document.addEventListener('DOMContentLoaded', () => {
});
initDiscussionFilters(store);
+ initSortDiscussions(store);
});
diff --git a/app/assets/javascripts/notes/sort_discussions.js b/app/assets/javascripts/notes/sort_discussions.js
new file mode 100644
index 00000000000..a06c23f5f76
--- /dev/null
+++ b/app/assets/javascripts/notes/sort_discussions.js
@@ -0,0 +1,16 @@
+import Vue from 'vue';
+import SortDiscussion from './components/sort_discussion.vue';
+
+export default store => {
+ const el = document.getElementById('js-vue-sort-issue-discussions');
+
+ if (!el) return null;
+
+ return new Vue({
+ el,
+ store,
+ render(createElement) {
+ return createElement(SortDiscussion);
+ },
+ });
+};
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index accc37121d0..1b80b59621a 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -69,6 +69,10 @@ export const updateDiscussion = ({ commit, state }, discussion) => {
return utils.findNoteObjectById(state.discussions, discussion.id);
};
+export const setDiscussionSortDirection = ({ commit }, direction) => {
+ commit(types.SET_DISCUSSIONS_SORT, direction);
+};
+
export const removeNote = ({ commit, dispatch, state }, note) => {
const discussion = state.discussions.find(({ id }) => id === note.discussion_id);
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index 28cc9cdd7e9..eb877083bca 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -1,8 +1,16 @@
-import { flattenDeep } from 'lodash';
+import { flattenDeep, clone } from 'lodash';
import * as constants from '../constants';
import { collapseSystemNotes } from './collapse_utils';
-export const discussions = state => collapseSystemNotes(state.discussions);
+export const discussions = state => {
+ let discussionsInState = clone(state.discussions);
+ // NOTE: not testing bc will be removed when backend is finished.
+ if (state.discussionSortOrder === constants.DESC) {
+ discussionsInState = discussionsInState.reverse();
+ }
+
+ return collapseSystemNotes(discussionsInState);
+};
export const convertedDisscussionIds = state => state.convertedDisscussionIds;
@@ -12,6 +20,13 @@ export const getNotesData = state => state.notesData;
export const isNotesFetched = state => state.isNotesFetched;
+/*
+ * WARNING: This is an example of an "unnecessary" getter
+ * more info found here: https://gitlab.com/groups/gitlab-org/-/epics/2913.
+ */
+
+export const sortDirection = state => state.discussionSortOrder;
+
export const isLoading = state => state.isLoading;
export const getNotesDataByProp = state => prop => state.notesData[prop];
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index 2d317dcd7da..81844ad6e98 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -1,10 +1,12 @@
import * as actions from '../actions';
import * as getters from '../getters';
import mutations from '../mutations';
+import { ASC } from '../../constants';
export default () => ({
state: {
discussions: [],
+ discussionSortOrder: ASC,
convertedDisscussionIds: [],
targetNoteHash: null,
lastFetchedAt: null,
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index 0cc59f9150c..5b7225bb3d2 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -27,6 +27,7 @@ export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION';
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';
// Issue
export const CLOSE_ISSUE = 'CLOSE_ISSUE';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 68bf8394508..028fc87198c 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -263,6 +263,10 @@ export default {
discussion.truncated_diff_lines = utils.prepareDiffLines(diffLines);
},
+ [types.SET_DISCUSSIONS_SORT](state, sort) {
+ state.discussionSortOrder = sort;
+ },
+
[types.DISABLE_COMMENTS](state, value) {
state.commentsDisabled = value;
},
diff --git a/app/assets/javascripts/vue_shared/components/ordered_layout.vue b/app/assets/javascripts/vue_shared/components/ordered_layout.vue
new file mode 100644
index 00000000000..117e79ca39f
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/ordered_layout.vue
@@ -0,0 +1,12 @@
+<script>
+export default {
+ functional: true,
+ render(h, context) {
+ const { slotKeys } = context.props;
+ const slots = context.slots();
+ const children = slotKeys.map(key => slots[key]).filter(x => x);
+
+ return children;
+ },
+};
+</script>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue
index 285a0fe9ffb..842b2fdbc43 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue
@@ -58,13 +58,13 @@ export default {
</script>
<template>
- <div class="labels-select-contents-create">
+ <div class="labels-select-contents-create js-labels-create">
<div class="dropdown-title d-flex align-items-center pt-0 pb-2">
<gl-button
:aria-label="__('Go back')"
variant="link"
size="sm"
- class="dropdown-header-button p-0"
+ class="js-btn-back dropdown-header-button p-0"
@click="toggleDropdownContentsCreateView"
>
<gl-icon name="arrow-left" />
@@ -116,7 +116,7 @@ export default {
<gl-loading-icon v-show="labelCreateInProgress" :inline="true" class="mr-1" />
{{ __('Create') }}
</gl-button>
- <gl-button class="pull-right" @click="toggleDropdownContentsCreateView">
+ <gl-button class="pull-right js-btn-cancel-create" @click="toggleDropdownContentsCreateView">
{{ __('Cancel') }}
</gl-button>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
index 7ec420fa908..a3494a9e38f 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
@@ -117,7 +117,7 @@ export default {
</script>
<template>
- <div class="labels-select-contents-list" @keydown="handleKeyDown">
+ <div class="labels-select-contents-list js-labels-list" @keydown="handleKeyDown">
<gl-loading-icon
v-if="labelsFetchInProgress"
class="labels-fetch-loading position-absolute d-flex align-items-center w-100 h-100"
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
index 5e41a155ef6..78102caacf5 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
@@ -1,4 +1,5 @@
<script>
+import $ from 'jquery';
import Vue from 'vue';
import Vuex, { mapState, mapActions } from 'vuex';
import { __ } from '~/locale';
@@ -149,9 +150,16 @@ export default {
* the dropdown while dropdown is visible.
*/
handleDocumentClick({ target }) {
+ // This approach of element detection is needed
+ // as the dropdown wrapper is not using `GlDropdown` as
+ // it will also require us to use `BDropdownForm`
+ // which is yet to be implemented in GitLab UI.
if (
this.showDropdownButton &&
this.showDropdownContents &&
+ !$(target).parents('.js-btn-back').length &&
+ !$(target).parents('.js-labels-list').length &&
+ !target?.classList.contains('js-btn-cancel-create') &&
!target?.classList.contains('js-sidebar-dropdown-toggle') &&
!this.$refs.dropdownButtonCollapsed?.$el.contains(target) &&
!this.$refs.dropdownContents?.$el.contains(target)