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-02-17 12:08:52 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-17 12:08:52 +0300
commit53ae6b7e3f83591ad251a3f771f5bf3b8cf087ba (patch)
tree5180b96d6a84f36a515cedfa8e81d72de5ccf4fb /app/assets
parentcfe63cce6a90a1c70397c1b9f6d90480f25cae0a (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_mermaid.js19
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts.js23
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_toggle.js22
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_toggle.vue60
-rw-r--r--app/assets/javascripts/blob/pdf/index.js8
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown.js2
-rw-r--r--app/assets/javascripts/notes/constants.js1
-rw-r--r--app/assets/javascripts/notes/mixins/description_version_history.js2
-rw-r--r--app/assets/javascripts/notes/stores/actions.js53
-rw-r--r--app/assets/javascripts/notes/stores/collapse_utils.js8
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js2
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js8
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js21
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/system_note.vue24
-rw-r--r--app/assets/stylesheets/pages/notes.scss13
16 files changed, 244 insertions, 24 deletions
diff --git a/app/assets/javascripts/behaviors/markdown/render_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
index c86a4c9f178..3856832de90 100644
--- a/app/assets/javascripts/behaviors/markdown/render_mermaid.js
+++ b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
@@ -1,4 +1,5 @@
import flash from '~/flash';
+import $ from 'jquery';
import { sprintf, __ } from '../../locale';
// Renders diagrams and flowcharts from text using Mermaid in any element with the
@@ -18,7 +19,7 @@ import { sprintf, __ } from '../../locale';
// This is an arbitrary number; Can be iterated upon when suitable.
const MAX_CHAR_LIMIT = 5000;
-export default function renderMermaid($els) {
+function renderMermaids($els) {
if (!$els.length) return;
// A diagram may have been truncated in search results which will cause errors, so abort the render.
@@ -95,3 +96,19 @@ export default function renderMermaid($els) {
flash(`Can't load mermaid module: ${err}`);
});
}
+
+export default function renderMermaid($els) {
+ if (!$els.length) return;
+
+ const visibleMermaids = $els.filter(function filter() {
+ return $(this).closest('details').length === 0;
+ });
+
+ renderMermaids(visibleMermaids);
+
+ $els.closest('details').one('toggle', function toggle() {
+ if (this.open) {
+ renderMermaids($(this).find('.js-render-mermaid'));
+ }
+ });
+}
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
index 66cb9fd7672..85636f3e5d2 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
@@ -1,6 +1,9 @@
import $ from 'jquery';
import Cookies from 'js-cookie';
import Mousetrap from 'mousetrap';
+import Vue from 'vue';
+import { disableShortcuts, shouldDisableShortcuts } from './shortcuts_toggle';
+import ShortcutsToggle from './shortcuts_toggle.vue';
import axios from '../../lib/utils/axios_utils';
import { refreshCurrentPage, visitUrl } from '../../lib/utils/url_utility';
import findAndFollowLink from '../../lib/utils/navigation_utility';
@@ -15,6 +18,15 @@ Mousetrap.stopCallback = (e, element, combo) => {
return defaultStopCallback(e, element, combo);
};
+function initToggleButton() {
+ return new Vue({
+ el: document.querySelector('.js-toggle-shortcuts'),
+ render(createElement) {
+ return createElement(ShortcutsToggle);
+ },
+ });
+}
+
export default class Shortcuts {
constructor() {
this.onToggleHelp = this.onToggleHelp.bind(this);
@@ -48,6 +60,14 @@ export default class Shortcuts {
$(this).remove();
e.preventDefault();
});
+
+ $('.js-shortcuts-modal-trigger')
+ .off('click')
+ .on('click', this.onToggleHelp);
+
+ if (shouldDisableShortcuts()) {
+ disableShortcuts();
+ }
}
onToggleHelp(e) {
@@ -104,7 +124,8 @@ export default class Shortcuts {
}
return $('.js-more-help-button').remove();
- });
+ })
+ .then(initToggleButton);
}
focusFilter(e) {
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_toggle.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_toggle.js
new file mode 100644
index 00000000000..66aa1b752ae
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_toggle.js
@@ -0,0 +1,22 @@
+import Mousetrap from 'mousetrap';
+import 'mousetrap/plugins/pause/mousetrap-pause';
+
+const shorcutsDisabledKey = 'shortcutsDisabled';
+
+export const shouldDisableShortcuts = () => {
+ try {
+ return localStorage.getItem(shorcutsDisabledKey) === 'true';
+ } catch (e) {
+ return false;
+ }
+};
+
+export function enableShortcuts() {
+ localStorage.setItem(shorcutsDisabledKey, false);
+ Mousetrap.unpause();
+}
+
+export function disableShortcuts() {
+ localStorage.setItem(shorcutsDisabledKey, true);
+ Mousetrap.pause();
+}
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_toggle.vue b/app/assets/javascripts/behaviors/shortcuts/shortcuts_toggle.vue
new file mode 100644
index 00000000000..a53b1b06be9
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_toggle.vue
@@ -0,0 +1,60 @@
+<script>
+import { GlToggle, GlSprintf } from '@gitlab/ui';
+import AccessorUtilities from '~/lib/utils/accessor';
+import { disableShortcuts, enableShortcuts, shouldDisableShortcuts } from './shortcuts_toggle';
+
+export default {
+ components: {
+ GlSprintf,
+ GlToggle,
+ },
+ data() {
+ return {
+ localStorageUsable: AccessorUtilities.isLocalStorageAccessSafe(),
+ shortcutsEnabled: !shouldDisableShortcuts(),
+ };
+ },
+ methods: {
+ onChange(value) {
+ this.shortcutsEnabled = value;
+ if (value) {
+ enableShortcuts();
+ } else {
+ disableShortcuts();
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="localStorageUsable" class="d-inline-flex align-items-center js-toggle-shortcuts">
+ <gl-toggle
+ v-model="shortcutsEnabled"
+ aria-describedby="shortcutsToggle"
+ class="prepend-left-10 mb-0"
+ label-position="right"
+ @change="onChange"
+ >
+ <template #labelOn>
+ <gl-sprintf
+ :message="__('%{screenreaderOnlyStart}Keyboard shorcuts%{screenreaderOnlyEnd} Enabled')"
+ >
+ <template #screenreaderOnly="{ content }">
+ <span class="sr-only">{{ content }}</span>
+ </template>
+ </gl-sprintf>
+ </template>
+ <template #labelOff>
+ <gl-sprintf
+ :message="__('%{screenreaderOnlyStart}Keyboard shorcuts%{screenreaderOnlyEnd} Disabled')"
+ >
+ <template #screenreaderOnly="{ content }">
+ <span class="sr-only">{{ content }}</span>
+ </template>
+ </gl-sprintf>
+ </template>
+ </gl-toggle>
+ <div id="shortcutsToggle" class="sr-only">{{ __('Enable or disable keyboard shortcuts') }}</div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/blob/pdf/index.js b/app/assets/javascripts/blob/pdf/index.js
index e9a0b3979e6..19778d07983 100644
--- a/app/assets/javascripts/blob/pdf/index.js
+++ b/app/assets/javascripts/blob/pdf/index.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import pdfLab from '../../pdf/index.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
export default () => {
const el = document.getElementById('js-pdf-viewer');
@@ -8,6 +9,7 @@ export default () => {
el,
components: {
pdfLab,
+ GlLoadingIcon,
},
data() {
return {
@@ -32,11 +34,7 @@ export default () => {
<div
class="text-center loading"
v-if="loading && !error">
- <i
- class="fa fa-spinner fa-spin"
- aria-hidden="true"
- aria-label="PDF loading">
- </i>
+ <gl-loading-icon class="mt-5" size="lg"/>
</div>
<pdf-lab
v-if="!loadError"
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
index 72565c2ca13..2b6e1f25dc6 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
@@ -12,7 +12,7 @@ export default class FilteredSearchDropdown {
this.filter = filter;
this.dropdown = dropdown;
this.loadingTemplate = `<div class="filter-dropdown-loading">
- <i class="fa fa-spinner fa-spin"></i>
+ <span class="spinner"></span>
</div>`;
this.bindEvents();
}
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index 68c117183a1..e9a81bc9553 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -18,6 +18,7 @@ export const HISTORY_ONLY_FILTER_VALUE = 2;
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 NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE,
diff --git a/app/assets/javascripts/notes/mixins/description_version_history.js b/app/assets/javascripts/notes/mixins/description_version_history.js
index 12d80f3faa2..66e6685cfd8 100644
--- a/app/assets/javascripts/notes/mixins/description_version_history.js
+++ b/app/assets/javascripts/notes/mixins/description_version_history.js
@@ -3,10 +3,12 @@
export default {
computed: {
canSeeDescriptionVersion() {},
+ canDeleteDescriptionVersion() {},
shouldShowDescriptionVersion() {},
descriptionVersionToggleIcon() {},
},
methods: {
toggleDescriptionVersion() {},
+ deleteDescriptionVersion() {},
},
};
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index f3dc6187c3f..594e3a14d56 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -491,23 +491,66 @@ export const convertToDiscussion = ({ commit }, noteId) =>
export const removeConvertedDiscussion = ({ commit }, noteId) =>
commit(types.REMOVE_CONVERTED_DISCUSSION, noteId);
-export const fetchDescriptionVersion = (_, { endpoint, startingVersion }) => {
+export const setCurrentDiscussionId = ({ commit }, discussionId) =>
+ commit(types.SET_CURRENT_DISCUSSION_ID, discussionId);
+
+export const fetchDescriptionVersion = ({ dispatch }, { endpoint, startingVersion }) => {
let requestUrl = endpoint;
if (startingVersion) {
requestUrl = mergeUrlParams({ start_version_id: startingVersion }, requestUrl);
}
+ dispatch('requestDescriptionVersion');
return axios
.get(requestUrl)
- .then(res => res.data)
- .catch(() => {
+ .then(res => {
+ dispatch('receiveDescriptionVersion', res.data);
+ })
+ .catch(error => {
+ dispatch('receiveDescriptionVersionError', error);
Flash(__('Something went wrong while fetching description changes. Please try again.'));
});
};
-export const setCurrentDiscussionId = ({ commit }, discussionId) =>
- commit(types.SET_CURRENT_DISCUSSION_ID, discussionId);
+export const requestDescriptionVersion = ({ commit }) => {
+ commit(types.REQUEST_DESCRIPTION_VERSION);
+};
+export const receiveDescriptionVersion = ({ commit }, descriptionVersion) => {
+ commit(types.RECEIVE_DESCRIPTION_VERSION, descriptionVersion);
+};
+export const receiveDescriptionVersionError = ({ commit }, error) => {
+ commit(types.RECEIVE_DESCRIPTION_VERSION_ERROR, error);
+};
+
+export const softDeleteDescriptionVersion = ({ dispatch }, { endpoint, startingVersion }) => {
+ let requestUrl = endpoint;
+
+ if (startingVersion) {
+ requestUrl = mergeUrlParams({ start_version_id: startingVersion }, requestUrl);
+ }
+ dispatch('requestDeleteDescriptionVersion');
+
+ return axios
+ .delete(requestUrl)
+ .then(() => {
+ dispatch('receiveDeleteDescriptionVersion');
+ })
+ .catch(error => {
+ dispatch('receiveDeleteDescriptionVersionError', error);
+ Flash(__('Something went wrong while deleting description changes. Please try again.'));
+ });
+};
+
+export const requestDeleteDescriptionVersion = ({ commit }) => {
+ commit(types.REQUEST_DELETE_DESCRIPTION_VERSION);
+};
+export const receiveDeleteDescriptionVersion = ({ commit }) => {
+ commit(types.RECEIVE_DELETE_DESCRIPTION_VERSION, __('Deleted'));
+};
+export const receiveDeleteDescriptionVersionError = ({ commit }, error) => {
+ commit(types.RECEIVE_DELETE_DESCRIPTION_VERSION_ERROR, error);
+};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/notes/stores/collapse_utils.js b/app/assets/javascripts/notes/stores/collapse_utils.js
index 3cdcc7a05b8..d94fc626a3f 100644
--- a/app/assets/javascripts/notes/stores/collapse_utils.js
+++ b/app/assets/javascripts/notes/stores/collapse_utils.js
@@ -1,4 +1,4 @@
-import { DESCRIPTION_TYPE } from '../constants';
+import { DESCRIPTION_TYPE, TIME_DIFFERENCE_VALUE } from '../constants';
/**
* Checks the time difference between two notes from their 'created_at' dates
@@ -45,7 +45,11 @@ export const collapseSystemNotes = notes => {
const timeDifferenceMinutes = getTimeDifferenceMinutes(lastDescriptionSystemNote, note);
// are they less than 10 minutes apart from the same user?
- if (timeDifferenceMinutes > 10 || note.author.id !== lastDescriptionSystemNote.author.id) {
+ if (
+ timeDifferenceMinutes > TIME_DIFFERENCE_VALUE ||
+ note.author.id !== lastDescriptionSystemNote.author.id ||
+ lastDescriptionSystemNote.description_version_deleted
+ ) {
// update the previous system note
lastDescriptionSystemNote = note;
lastDescriptionSystemNoteIndex = acc.length;
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index 771b80108b8..0e991f2f4f0 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -14,6 +14,7 @@ export default () => ({
isToggleStateButtonLoading: false,
isNotesFetched: false,
isLoading: true,
+ isLoadingDescriptionVersion: false,
// holds endpoints and permissions provided through haml
notesData: {
@@ -27,6 +28,7 @@ export default () => ({
commentsDisabled: false,
resolvableDiscussionsCount: 0,
unresolvedDiscussionsCount: 0,
+ descriptionVersion: null,
},
actions,
getters,
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index 8eb426d3f9b..6554aee0d5b 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -31,3 +31,11 @@ export const SET_CURRENT_DISCUSSION_ID = 'SET_CURRENT_DISCUSSION_ID';
export const CLOSE_ISSUE = 'CLOSE_ISSUE';
export const REOPEN_ISSUE = 'REOPEN_ISSUE';
export const TOGGLE_STATE_BUTTON_LOADING = 'TOGGLE_STATE_BUTTON_LOADING';
+
+// Description version
+export const REQUEST_DESCRIPTION_VERSION = 'REQUEST_DESCRIPTION_VERSION';
+export const RECEIVE_DESCRIPTION_VERSION = 'RECEIVE_DESCRIPTION_VERSION';
+export const RECEIVE_DESCRIPTION_VERSION_ERROR = 'RECEIVE_DESCRIPTION_VERSION_ERROR';
+export const REQUEST_DELETE_DESCRIPTION_VERSION = 'REQUEST_DELETE_DESCRIPTION_VERSION';
+export const RECEIVE_DELETE_DESCRIPTION_VERSION = 'RECEIVE_DELETE_DESCRIPTION_VERSION';
+export const RECEIVE_DELETE_DESCRIPTION_VERSION_ERROR = 'RECEIVE_DELETE_DESCRIPTION_VERSION_ERROR';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 71091d26b85..d32a88e4c71 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -284,4 +284,25 @@ export default {
[types.SET_CURRENT_DISCUSSION_ID](state, discussionId) {
state.currentDiscussionId = discussionId;
},
+
+ [types.REQUEST_DESCRIPTION_VERSION](state) {
+ state.isLoadingDescriptionVersion = true;
+ },
+ [types.RECEIVE_DESCRIPTION_VERSION](state, descriptionVersion) {
+ state.isLoadingDescriptionVersion = false;
+ state.descriptionVersion = descriptionVersion;
+ },
+ [types.RECEIVE_DESCRIPTION_VERSION_ERROR](state) {
+ state.isLoadingDescriptionVersion = false;
+ },
+ [types.REQUEST_DELETE_DESCRIPTION_VERSION](state) {
+ state.isLoadingDescriptionVersion = true;
+ },
+ [types.RECEIVE_DELETE_DESCRIPTION_VERSION](state, descriptionVersion) {
+ state.isLoadingDescriptionVersion = false;
+ state.descriptionVersion = descriptionVersion;
+ },
+ [types.RECEIVE_DELETE_DESCRIPTION_VERSION_ERROR](state) {
+ state.isLoadingDescriptionVersion = false;
+ },
};
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index 7e14b810c13..d9192d3d76b 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -132,7 +132,7 @@ export default () => {
});
axios
- .get(dataset.testReportEndpoint)
+ .get(dataset.testReportsCountEndpoint)
.then(({ data }) => {
document.querySelector('.js-test-report-badge-counter').innerHTML = data.total_count;
})
diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
index 15ca64ba297..0c4d75fb0ad 100644
--- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
@@ -17,11 +17,12 @@
* />
*/
import $ from 'jquery';
-import { mapGetters, mapActions } from 'vuex';
-import { GlSkeletonLoading } from '@gitlab/ui';
+import { mapGetters, mapActions, mapState } from 'vuex';
+import { GlButton, GlSkeletonLoading, GlTooltipDirective } from '@gitlab/ui';
import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
import noteHeader from '~/notes/components/note_header.vue';
import Icon from '~/vue_shared/components/icon.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import TimelineEntryItem from './timeline_entry_item.vue';
import { spriteIcon } from '../../../lib/utils/common_utils';
import initMRPopovers from '~/mr_popover/';
@@ -34,9 +35,13 @@ export default {
Icon,
noteHeader,
TimelineEntryItem,
+ GlButton,
GlSkeletonLoading,
},
- mixins: [descriptionVersionHistoryMixin],
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ mixins: [descriptionVersionHistoryMixin, glFeatureFlagsMixin()],
props: {
note: {
type: Object,
@@ -50,6 +55,7 @@ export default {
},
computed: {
...mapGetters(['targetNoteHash']),
+ ...mapState(['descriptionVersion', 'isLoadingDescriptionVersion']),
noteAnchorId() {
return `note_${this.note.id}`;
},
@@ -80,7 +86,7 @@ export default {
initMRPopovers(this.$el.querySelectorAll('.gfm-merge_request'));
},
methods: {
- ...mapActions(['fetchDescriptionVersion']),
+ ...mapActions(['fetchDescriptionVersion', 'softDeleteDescriptionVersion']),
},
};
</script>
@@ -122,6 +128,16 @@ export default {
<gl-skeleton-loading />
</pre>
<pre v-else class="wrapper mt-2" v-html="descriptionVersion"></pre>
+ <gl-button
+ v-if="canDeleteDescriptionVersion"
+ ref="deleteDescriptionVersionButton"
+ v-gl-tooltip
+ :title="__('Remove description history')"
+ class="btn-transparent delete-description-history"
+ @click="deleteDescriptionVersion"
+ >
+ <icon name="remove" />
+ </gl-button>
</div>
</div>
</div>
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 1da9f691639..1a06ae1ed41 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -311,13 +311,18 @@ $note-form-margin-left: 72px;
overflow: hidden;
.description-version {
+ position: relative;
+
+ .btn.delete-description-history {
+ position: absolute;
+ top: 18px;
+ right: 0;
+ }
+
pre {
max-height: $dropdown-max-height-lg;
white-space: pre-wrap;
-
- &.loading-state {
- height: 94px;
- }
+ padding-right: 30px;
}
}