diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-17 09:09:43 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-17 09:09:43 +0000 |
commit | fe29f106cd41b3c57b0266f3de6470cffee587ea (patch) | |
tree | f8d9cbc771b4a642c2fe59929dd6cc07a922cadd /app/assets/javascripts | |
parent | 6110935892876a26d8dfcb919d8c955c92ecc1e5 (diff) | |
download | gitlab-ce-fe29f106cd41b3c57b0266f3de6470cffee587ea.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
17 files changed, 180 insertions, 104 deletions
diff --git a/app/assets/javascripts/boards/components/modal/header.vue b/app/assets/javascripts/boards/components/modal/header.vue index a42e691dcf3..8eae8e4726f 100644 --- a/app/assets/javascripts/boards/components/modal/header.vue +++ b/app/assets/javascripts/boards/components/modal/header.vue @@ -72,7 +72,7 @@ export default { <button ref="selectAllBtn" type="button" - class="btn btn-success btn-inverted prepend-left-10" + class="btn btn-success btn-inverted gl-ml-3" @click="toggleAll" > {{ selectAllText }} diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 87327e40167..5b4a1d262dd 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -352,7 +352,7 @@ export default () => { template: ` <div class="board-extra-actions"> <button - class="btn btn-success prepend-left-10" + class="btn btn-success gl-ml-3" type="button" data-placement="bottom" ref="addIssuesButton" diff --git a/app/assets/javascripts/boards/toggle_focus.js b/app/assets/javascripts/boards/toggle_focus.js index a437a34c948..e60e7059192 100644 --- a/app/assets/javascripts/boards/toggle_focus.js +++ b/app/assets/javascripts/boards/toggle_focus.js @@ -25,7 +25,7 @@ export default (ModalStore, boardsStore) => { <div class="board-extra-actions"> <a href="#" - class="btn btn-default has-tooltip prepend-left-10 js-focus-mode-btn" + class="btn btn-default has-tooltip gl-ml-3 js-focus-mode-btn" data-qa-selector="focus_mode_button" role="button" aria-label="Toggle focus mode" diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 55a0e91b0f3..108cc8d3a78 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -32,6 +32,7 @@ export default class FilteredSearchManager { filteredSearchTokenKeys = IssuableFilteredSearchTokenKeys, stateFiltersSelector = '.issues-state-filters', placeholder = __('Search or filter results...'), + anchor = null, }) { this.isGroup = isGroup; this.isGroupAncestor = isGroupAncestor; @@ -47,6 +48,7 @@ export default class FilteredSearchManager { this.filteredSearchTokenKeys = filteredSearchTokenKeys; this.stateFiltersSelector = stateFiltersSelector; this.placeholder = placeholder; + this.anchor = anchor; const { multipleAssignees } = this.filteredSearchInput.dataset; if (multipleAssignees && this.filteredSearchTokenKeys.enableMultipleAssignees) { @@ -779,7 +781,11 @@ export default class FilteredSearchManager { paths.push(`search=${sanitized}`); } - const parameterizedUrl = `?scope=all&utf8=%E2%9C%93&${paths.join('&')}`; + let parameterizedUrl = `?scope=all&utf8=%E2%9C%93&${paths.join('&')}`; + + if (this.anchor) { + parameterizedUrl += `#${this.anchor}`; + } if (this.updateObject) { this.updateObject(parameterizedUrl); diff --git a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue index 6b0aa5b2b2b..b37c7280a30 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue @@ -83,7 +83,7 @@ export default { <ul class="nav-links"> <li> {{ __('Commit Message') }} - <span v-popover="$options.popoverOptions" class="form-text text-muted prepend-left-10"> + <span v-popover="$options.popoverOptions" class="form-text text-muted gl-ml-3"> <icon name="question" /> </span> </li> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/new_merge_request_option.vue b/app/assets/javascripts/ide/components/commit_sidebar/new_merge_request_option.vue index 0812599c25c..cdf49866982 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/new_merge_request_option.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/new_merge_request_option.vue @@ -44,7 +44,7 @@ export default { data-qa-selector="start_new_mr_checkbox" @change="toggleShouldCreateMR" /> - <span class="prepend-left-10 ide-option-label"> + <span class="gl-ml-3 ide-option-label"> {{ __('Start a new merge request') }} </span> </label> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue index a9591805261..aed7b792902 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue @@ -66,7 +66,7 @@ export default { name="commit-action" @change="updateCommitAction($event.target.value)" /> - <span class="prepend-left-10"> + <span class="gl-ml-3"> <span v-if="label" class="ide-option-label"> {{ label }} </span> <slot v-else></slot> </span> </label> diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index 56d82597573..9bf8cffe940 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -147,10 +147,10 @@ export default { return getEndLineNumber(this.lineRange); }, showMultiLineComment() { - if (!this.glFeatures.multilineComments) return false; + if (!this.glFeatures.multilineComments || !this.discussionRoot) return false; if (this.isEditing) return true; - return this.line && this.discussionRoot && this.startLineNumber !== this.endLineNumber; + return this.line && this.startLineNumber !== this.endLineNumber; }, commentLineOptions() { if (!this.diffFile || !this.line) return []; diff --git a/app/assets/javascripts/pages/constants.js b/app/assets/javascripts/pages/constants.js index 5e119454ce1..35c67190b62 100644 --- a/app/assets/javascripts/pages/constants.js +++ b/app/assets/javascripts/pages/constants.js @@ -4,4 +4,5 @@ export const FILTERED_SEARCH = { MERGE_REQUESTS: 'merge_requests', ISSUES: 'issues', ADMIN_RUNNERS: 'admin/runners', + GROUP_RUNNERS_ANCHOR: 'runners-settings', }; diff --git a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js index 479c82265f2..23283f46a5d 100644 --- a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js @@ -1,11 +1,20 @@ import initSettingsPanels from '~/settings_panels'; import AjaxVariableList from '~/ci_variable_list/ajax_variable_list'; import initVariableList from '~/ci_variable_list'; +import initFilteredSearch from '~/pages/search/init_filtered_search'; +import AdminRunnersFilteredSearchTokenKeys from '~/filtered_search/admin_runners_filtered_search_token_keys'; +import { FILTERED_SEARCH } from '~/pages/constants'; document.addEventListener('DOMContentLoaded', () => { // Initialize expandable settings panels initSettingsPanels(); + initFilteredSearch({ + page: FILTERED_SEARCH.ADMIN_RUNNERS, + filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys, + anchor: FILTERED_SEARCH.GROUP_RUNNERS_ANCHOR, + }); + if (gon.features.newVariablesUi) { initVariableList(); } else { diff --git a/app/assets/javascripts/pages/search/init_filtered_search.js b/app/assets/javascripts/pages/search/init_filtered_search.js index e54e32199f0..b331a2bee6a 100644 --- a/app/assets/javascripts/pages/search/init_filtered_search.js +++ b/app/assets/javascripts/pages/search/init_filtered_search.js @@ -7,6 +7,7 @@ export default ({ isGroupAncestor, isGroupDecendent, stateFiltersSelector, + anchor, }) => { const filteredSearchEnabled = FilteredSearchManager && document.querySelector('.filtered-search'); if (filteredSearchEnabled) { @@ -17,6 +18,7 @@ export default ({ isGroupDecendent, filteredSearchTokenKeys, stateFiltersSelector, + anchor, }); filteredSearchManager.setup(); } diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue index 37dbd5b5e39..c01f9524ca8 100644 --- a/app/assets/javascripts/snippets/components/edit.vue +++ b/app/assets/javascripts/snippets/components/edit.vue @@ -3,9 +3,8 @@ import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import Flash from '~/flash'; import { __, sprintf } from '~/locale'; -import axios from '~/lib/utils/axios_utils'; import TitleField from '~/vue_shared/components/form/title.vue'; -import { getBaseURL, joinPaths, redirectTo } from '~/lib/utils/url_utility'; +import { redirectTo } from '~/lib/utils/url_utility'; import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue'; import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql'; @@ -15,6 +14,9 @@ import { SNIPPET_VISIBILITY_PRIVATE, SNIPPET_CREATE_MUTATION_ERROR, SNIPPET_UPDATE_MUTATION_ERROR, + SNIPPET_BLOB_ACTION_CREATE, + SNIPPET_BLOB_ACTION_UPDATE, + SNIPPET_BLOB_ACTION_MOVE, } from '../constants'; import SnippetBlobEdit from './snippet_blob_edit.vue'; import SnippetVisibilityEdit from './snippet_visibility_edit.vue'; @@ -53,18 +55,25 @@ export default { }, data() { return { - blob: {}, - fileName: '', - content: '', - originalContent: '', - isContentLoading: true, + blobsActions: {}, isUpdating: false, newSnippet: false, }; }, computed: { + getActionsEntries() { + return Object.values(this.blobsActions); + }, + allBlobsHaveContent() { + const entries = this.getActionsEntries; + return entries.length > 0 && !entries.find(action => !action.content); + }, + allBlobChangesRegistered() { + const entries = this.getActionsEntries; + return entries.length > 0 && !entries.find(action => action.action === ''); + }, updatePrevented() { - return this.snippet.title === '' || this.content === '' || this.isUpdating; + return this.snippet.title === '' || !this.allBlobsHaveContent || this.isUpdating; }, isProjectSnippet() { return Boolean(this.projectPath); @@ -75,8 +84,7 @@ export default { title: this.snippet.title, description: this.snippet.description, visibilityLevel: this.snippet.visibilityLevel, - fileName: this.fileName, - content: this.content, + files: this.getActionsEntries.filter(entry => entry.action !== ''), }; }, saveButtonLabel() { @@ -108,16 +116,47 @@ export default { onBeforeUnload(e = {}) { const returnValue = __('Are you sure you want to lose unsaved changes?'); - if (!this.hasChanges()) return undefined; + if (!this.allBlobChangesRegistered) return undefined; Object.assign(e, { returnValue }); return returnValue; }, - hasChanges() { - return this.content !== this.originalContent; - }, - updateFileName(newName) { - this.fileName = newName; + updateBlobActions(args = {}) { + // `_constants` is the internal prop that + // should not be sent to the mutation. Hence we filter it out from + // the argsToUpdateAction that is the data-basis for the mutation. + const { _constants: blobConstants, ...argsToUpdateAction } = args; + const { previousPath, filePath, content } = argsToUpdateAction; + let actionEntry = this.blobsActions[blobConstants.id] || {}; + let tunedActions = { + action: '', + previousPath, + }; + + if (this.newSnippet) { + // new snippet, hence new blob + tunedActions = { + action: SNIPPET_BLOB_ACTION_CREATE, + previousPath: '', + }; + } else if (previousPath && filePath) { + // renaming of a blob + renaming & content update + const renamedToOriginal = filePath === blobConstants.originalPath; + tunedActions = { + action: renamedToOriginal ? SNIPPET_BLOB_ACTION_UPDATE : SNIPPET_BLOB_ACTION_MOVE, + previousPath: !renamedToOriginal ? blobConstants.originalPath : '', + }; + } else if (content !== blobConstants.originalContent) { + // content update only + tunedActions = { + action: SNIPPET_BLOB_ACTION_UPDATE, + previousPath: '', + }; + } + + actionEntry = { ...actionEntry, ...argsToUpdateAction, ...tunedActions }; + + this.$set(this.blobsActions, blobConstants.id, actionEntry); }, flashAPIFailure(err) { const defaultErrorMsg = this.newSnippet @@ -129,26 +168,9 @@ export default { onNewSnippetFetched() { this.newSnippet = true; this.snippet = this.$options.newSnippetSchema; - this.blob = this.snippet.blob; - this.isContentLoading = false; }, onExistingSnippetFetched() { this.newSnippet = false; - const { blob } = this.snippet; - this.blob = blob; - this.fileName = blob.name; - const baseUrl = getBaseURL(); - const url = joinPaths(baseUrl, blob.rawPath); - - axios - .get(url) - .then(res => { - this.originalContent = res.data; - this.content = res.data; - - this.isContentLoading = false; - }) - .catch(e => this.flashAPIFailure(e)); }, onSnippetFetch(snippetRes) { if (snippetRes.data.snippets.edges.length === 0) { @@ -205,7 +227,6 @@ export default { title: '', description: '', visibilityLevel: SNIPPET_VISIBILITY_PRIVATE, - blob: {}, }, }; </script> @@ -236,12 +257,16 @@ export default { :markdown-preview-path="markdownPreviewPath" :markdown-docs-path="markdownDocsPath" /> - <snippet-blob-edit - v-model="content" - :file-name="fileName" - :is-loading="isContentLoading" - @name-change="updateFileName" - /> + <template v-if="blobs.length"> + <snippet-blob-edit + v-for="blob in blobs" + :key="blob.name" + :blob="blob" + @blob-updated="updateBlobActions" + /> + </template> + <snippet-blob-edit v-else @blob-updated="updateBlobActions" /> + <snippet-visibility-edit v-model="snippet.visibilityLevel" :help-link="visibilityHelpLink" diff --git a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue index 62c29b0c7cd..3c2dbfff6e1 100644 --- a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue +++ b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue @@ -2,6 +2,17 @@ import BlobHeaderEdit from '~/blob/components/blob_edit_header.vue'; import BlobContentEdit from '~/blob/components/blob_edit_content.vue'; import { GlLoadingIcon } from '@gitlab/ui'; +import { getBaseURL, joinPaths } from '~/lib/utils/url_utility'; +import axios from '~/lib/utils/axios_utils'; +import { SNIPPET_BLOB_CONTENT_FETCH_ERROR } from '~/snippets/constants'; +import Flash from '~/flash'; +import { sprintf } from '~/locale'; + +function localId() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); +} export default { components: { @@ -11,20 +22,70 @@ export default { }, inheritAttrs: false, props: { - value: { - type: String, + blob: { + type: Object, required: false, - default: '', + default: null, + validator: ({ rawPath }) => Boolean(rawPath), }, - fileName: { - type: String, - required: false, - default: '', + }, + data() { + return { + id: localId(), + filePath: this.blob?.path || '', + previousPath: '', + originalPath: this.blob?.path || '', + content: this.blob?.content || '', + originalContent: '', + isContentLoading: this.blob, + }; + }, + watch: { + filePath(filePath, previousPath) { + this.previousPath = previousPath; + this.notifyAboutUpdates({ previousPath }); }, - isLoading: { - type: Boolean, - required: false, - default: true, + content() { + this.notifyAboutUpdates(); + }, + }, + mounted() { + if (this.blob) { + this.fetchBlobContent(); + } + }, + methods: { + notifyAboutUpdates(args = {}) { + const { filePath, previousPath } = args; + this.$emit('blob-updated', { + filePath: filePath || this.filePath, + previousPath: previousPath || this.previousPath, + content: this.content, + _constants: { + originalPath: this.originalPath, + originalContent: this.originalContent, + id: this.id, + }, + }); + }, + fetchBlobContent() { + const baseUrl = getBaseURL(); + const url = joinPaths(baseUrl, this.blob.rawPath); + + axios + .get(url) + .then(res => { + this.originalContent = res.data; + this.content = res.data; + }) + .catch(e => this.flashAPIFailure(e)) + .finally(() => { + this.isContentLoading = false; + }); + }, + flashAPIFailure(err) { + Flash(sprintf(SNIPPET_BLOB_CONTENT_FETCH_ERROR, { err })); + this.isContentLoading = false; }, }, }; @@ -33,23 +94,14 @@ export default { <div class="form-group file-editor"> <label>{{ s__('Snippets|File') }}</label> <div class="file-holder snippet"> - <blob-header-edit - :value="fileName" - data-qa-selector="file_name_field" - @input="$emit('name-change', $event)" - /> + <blob-header-edit v-model="filePath" data-qa-selector="file_name_field" /> <gl-loading-icon - v-if="isLoading" + v-if="isContentLoading" :label="__('Loading snippet')" size="lg" class="loading-animation prepend-top-20 append-bottom-20" /> - <blob-content-edit - v-else - :value="value" - :file-name="fileName" - @input="$emit('input', $event)" - /> + <blob-content-edit v-else v-model="content" :file-name="filePath" /> </div> </div> </template> diff --git a/app/assets/javascripts/snippets/constants.js b/app/assets/javascripts/snippets/constants.js index b3abc73557c..99ee698408d 100644 --- a/app/assets/javascripts/snippets/constants.js +++ b/app/assets/javascripts/snippets/constants.js @@ -25,3 +25,8 @@ export const SNIPPET_VISIBILITY = { export const SNIPPET_CREATE_MUTATION_ERROR = __("Can't create snippet: %{err}"); export const SNIPPET_UPDATE_MUTATION_ERROR = __("Can't update snippet: %{err}"); +export const SNIPPET_BLOB_CONTENT_FETCH_ERROR = __("Can't fetch content for the blob: %{err}"); + +export const SNIPPET_BLOB_ACTION_CREATE = 'create'; +export const SNIPPET_BLOB_ACTION_UPDATE = 'update'; +export const SNIPPET_BLOB_ACTION_MOVE = 'move'; diff --git a/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql b/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql index a5425c7e87b..2cca71708ca 100644 --- a/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql +++ b/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql @@ -26,21 +26,6 @@ fragment SnippetBase on Snippet { ...BlobViewer } } - blob { - binary - name - path - rawPath - size - externalStorage - renderedAsText - simpleViewer { - ...BlobViewer - } - richViewer { - ...BlobViewer - } - } userPermissions { adminSnippet updateSnippet diff --git a/app/assets/javascripts/snippets/mixins/snippets.js b/app/assets/javascripts/snippets/mixins/snippets.js index 87a00cb8e90..91331cdf339 100644 --- a/app/assets/javascripts/snippets/mixins/snippets.js +++ b/app/assets/javascripts/snippets/mixins/snippets.js @@ -1,5 +1,7 @@ import GetSnippetQuery from '../queries/snippet.query.graphql'; +const blobsDefault = []; + export const getSnippetMixin = { apollo: { snippet: { @@ -11,7 +13,7 @@ export const getSnippetMixin = { }, update: data => data.snippets.edges[0]?.node, result(res) { - this.blobs = res.data.snippets.edges[0].node.blobs; + this.blobs = res.data.snippets.edges[0]?.node?.blobs || blobsDefault; if (this.onSnippetFetch) { this.onSnippetFetch(res); } @@ -28,7 +30,7 @@ export const getSnippetMixin = { return { snippet: {}, newSnippet: false, - blobs: [], + blobs: blobsDefault, }; }, computed: { diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue index 8c6d795f4a6..e053a9ddaa6 100644 --- a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue +++ b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue @@ -8,11 +8,7 @@ import { truncateNamespace } from '~/lib/utils/text_utility'; export default { name: 'ProjectListItem', - components: { - Icon, - ProjectAvatar, - GlDeprecatedButton, - }, + components: { Icon, ProjectAvatar, GlDeprecatedButton }, props: { project: { type: Object, @@ -22,15 +18,8 @@ export default { isString(p.name) && (isString(p.name_with_namespace) || isString(p.nameWithNamespace)), }, - selected: { - type: Boolean, - required: true, - }, - matcher: { - type: String, - required: false, - default: '', - }, + selected: { type: Boolean, required: true }, + matcher: { type: String, required: false, default: '' }, }, computed: { projectNameWithNamespace() { @@ -56,7 +45,7 @@ export default { @click="onClick" > <icon - class="prepend-left-10 gl-mr-3 flex-shrink-0 position-top-0 js-selected-icon" + class="gl-ml-3 gl-mr-3 flex-shrink-0 position-top-0 js-selected-icon" :class="{ 'js-selected visible': selected, 'js-unselected invisible': !selected }" name="mobile-issue-close" /> |