diff options
Diffstat (limited to 'app/assets/javascripts')
15 files changed, 172 insertions, 55 deletions
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index dd3feedbc0e..9f6d9a853da 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -244,6 +244,7 @@ class List { issue.project = data.project; issue.path = data.real_path; issue.referencePath = data.reference_path; + issue.assignableLabelsEndpoint = data.assignable_labels_endpoint; if (this.issuesSize > 1) { const moveBeforeId = this.issues[1].id; diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index aff32d95db1..b1f992c03ff 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -32,6 +32,7 @@ export default class Clusters { installKnativePath, installPrometheusPath, managePrometheusPath, + hasRbac, clusterType, clusterStatus, clusterStatusReason, @@ -45,6 +46,7 @@ export default class Clusters { this.store.setManagePrometheusPath(managePrometheusPath); this.store.updateStatus(clusterStatus); this.store.updateStatusReason(clusterStatusReason); + this.store.updateRbac(hasRbac); this.service = new ClustersService({ endpoint: statusPath, installHelmEndpoint: installHelmPath, @@ -102,6 +104,7 @@ export default class Clusters { ingressHelpPath: this.state.ingressHelpPath, managePrometheusPath: this.state.managePrometheusPath, ingressDnsHelpPath: this.state.ingressDnsHelpPath, + rbac: this.state.rbac, }, }); }, diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 489615f1f78..5d19c79570a 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -52,6 +52,11 @@ export default { required: false, default: '', }, + rbac: { + type: Boolean, + required: false, + default: false, + }, }, data: () => ({ elasticsearchLogo, @@ -442,6 +447,18 @@ export default { title-link="https://github.com/knative/docs" > <div slot="description"> + <span v-if="!rbac"> + <p v-if="!rbac" class="bs-callout bs-callout-info append-bottom-0"> + {{ + s__(`ClusterIntegration|You must have an RBAC-enabled cluster + to install Knative.`) + }} + <a :href="helpPath" target="_blank" rel="noopener noreferrer"> + {{ __('More information') }} + </a> + </p> + <br /> + </span> <p> {{ s__(`ClusterIntegration|Knative extends Kubernetes to provide @@ -465,7 +482,7 @@ export default { /> </div> </template> - <template v-else-if="helmInstalled"> + <template v-else-if="helmInstalled && rbac"> <div class="form-group"> <label for="knative-domainname"> {{ s__('ClusterIntegration|Knative Domain Name:') }} diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index c750daab112..8f74be4e0e6 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -1,4 +1,5 @@ import { s__ } from '../../locale'; +import { parseBoolean } from '../../lib/utils/common_utils'; import { INGRESS, JUPYTER, KNATIVE, CERT_MANAGER } from '../constants'; export default class ClusterStore { @@ -7,6 +8,7 @@ export default class ClusterStore { helpPath: null, ingressHelpPath: null, status: null, + rbac: false, statusReason: null, applications: { helm: { @@ -81,6 +83,10 @@ export default class ClusterStore { this.state.status = status; } + updateRbac(rbac) { + this.state.rbac = parseBoolean(rbac); + } + updateStatusReason(reason) { this.state.statusReason = reason; } diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue index ebc4a83af4d..c02a8740a42 100644 --- a/app/assets/javascripts/diffs/components/commit_item.vue +++ b/app/assets/javascripts/diffs/components/commit_item.vue @@ -5,6 +5,7 @@ import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import CIIcon from '~/vue_shared/components/ci_icon.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; +import initUserPopovers from '../../user_popovers'; /** * CommitItem @@ -35,20 +36,30 @@ export default { }, }, computed: { + author() { + return this.commit.author || {}; + }, authorName() { - return (this.commit.author && this.commit.author.name) || this.commit.author_name; + return this.author.name || this.commit.author_name; + }, + authorClass() { + return this.author.name ? 'js-user-link' : ''; + }, + authorId() { + return this.author.id ? this.author.id : ''; }, authorUrl() { - return ( - (this.commit.author && this.commit.author.web_url) || `mailto:${this.commit.author_email}` - ); + return this.author.web_url || `mailto:${this.commit.author_email}`; }, authorAvatar() { - return ( - (this.commit.author && this.commit.author.avatar_url) || this.commit.author_gravatar_url - ); + return this.author.avatar_url || this.commit.author_gravatar_url; }, }, + created() { + this.$nextTick(() => { + initUserPopovers(this.$el.querySelectorAll('.js-user-link')); + }); + }, }; </script> @@ -81,7 +92,13 @@ export default { </button> <div class="commiter"> - <a :href="authorUrl" v-text="authorName"></a> {{ s__('CommitWidget|authored') }} + <a + :href="authorUrl" + :class="authorClass" + :data-user-id="authorId" + v-text="authorName" + ></a> + {{ s__('CommitWidget|authored') }} <time-ago-tooltip :time="commit.authored_date" /> </div> diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index f842d2d74db..f5e2e46237f 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -51,8 +51,6 @@ export default class GLForm { // form and textarea event listeners this.addEventListeners(); addMarkdownListeners(this.form); - // hide discard button - this.form.find('.js-note-discard').hide(); this.form.show(); if (this.isAutosizeable) this.setupAutosize(); } diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index dfb53c986fc..c3443c300e3 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -138,8 +138,6 @@ export default class Notes { this.$wrapperEl.on('click', '.js-note-delete', this.removeNote); // delete note attachment this.$wrapperEl.on('click', '.js-note-attachment-delete', this.removeAttachment); - // reset main target form when clicking discard - this.$wrapperEl.on('click', '.js-note-discard', this.resetMainTargetForm); // update the file name when an attachment is selected this.$wrapperEl.on('change', '.js-note-attachment-input', this.updateFormAttachment); // reply to diff/discussion notes @@ -191,7 +189,6 @@ export default class Notes { this.$wrapperEl.off('keyup input', '.js-note-text'); this.$wrapperEl.off('click', '.js-note-target-reopen'); this.$wrapperEl.off('click', '.js-note-target-close'); - this.$wrapperEl.off('click', '.js-note-discard'); this.$wrapperEl.off('keydown', '.js-note-text'); this.$wrapperEl.off('click', '.js-comment-resolve-button'); this.$wrapperEl.off('click', '.system-note-commit-list-toggler'); @@ -986,11 +983,9 @@ export default class Notes { form.find('#note_position').val(dataHolder.attr('data-position')); form - .find('.js-note-discard') + .find('.js-close-discussion-note-form') .show() - .removeClass('js-note-discard') - .addClass('js-close-discussion-note-form') - .text(form.find('.js-close-discussion-note-form').data('cancelText')); + .removeClass('hide'); form.find('.js-note-target-close').remove(); form.find('.js-note-new-discussion').remove(); this.setupNoteForm(form); @@ -1194,12 +1189,11 @@ export default class Notes { } updateTargetButtons(e) { - var closebtn, closetext, discardbtn, form, reopenbtn, reopentext, textarea; + var closebtn, closetext, form, reopenbtn, reopentext, textarea; textarea = $(e.target); form = textarea.parents('form'); reopenbtn = form.find('.js-note-target-reopen'); closebtn = form.find('.js-note-target-close'); - discardbtn = form.find('.js-note-discard'); if (textarea.val().trim().length > 0) { reopentext = reopenbtn.attr('data-alternative-text'); @@ -1216,9 +1210,6 @@ export default class Notes { if (closebtn.is(':not(.btn-comment-and-close)')) { closebtn.addClass('btn-comment-and-close'); } - if (discardbtn.is(':hidden')) { - return discardbtn.show(); - } } else { reopentext = reopenbtn.data('originalText'); closetext = closebtn.data('originalText'); @@ -1234,9 +1225,6 @@ export default class Notes { if (closebtn.is('.btn-comment-and-close')) { closebtn.removeClass('btn-comment-and-close'); } - if (discardbtn.is(':visible')) { - return discardbtn.hide(); - } } } diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index ce56beb1e6b..8bf02327cd2 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -431,15 +431,6 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" :label="issueActionButtonTitle" @click="handleSave(true);" /> - - <button - v-if="note.length" - type="button" - class="btn btn-cancel js-note-discard" - @click="discard" - > - Discard draft - </button> </div> </form> </div> diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index e78596f8b52..db62ddb3ecd 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -149,6 +149,9 @@ export default { return shouldResolve || shouldToggleState; }, + handleKeySubmit() { + this.handleUpdate(); + }, handleUpdate(shouldResolve) { const beforeSubmitDiscussionState = this.discussionResolved; this.isSubmitting = true; @@ -216,8 +219,8 @@ export default { class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input" aria-label="Description" placeholder="Write a comment or drag your files hereā¦" - @keydown.meta.enter="handleUpdate();" - @keydown.ctrl.enter="handleUpdate();" + @keydown.meta.enter="handleKeySubmit();" + @keydown.ctrl.enter="handleKeySubmit();" @keydown.up="editMyLastNote();" @keydown.esc="cancelHandler(true);" ></textarea> diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue index 08c7719dcf2..19d9903c988 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue @@ -325,8 +325,8 @@ export default { <project-setting-row v-if="pagesAvailable && pagesAccessControlEnabled" :help-path="pagesHelpPath" - label="Pages" - help-text="Static website for the project." + label="Pages access control" + help-text="Access control for the project's static website" > <project-feature-setting v-model="pagesAccessLevel" diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue index 7d8863dff29..918622ef8dc 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue @@ -1,8 +1,20 @@ <script> import { GlLink, GlTooltipDirective } from '@gitlab/ui'; +import _ from 'underscore'; +import { __, sprintf } from '~/locale'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import popover from '~/vue_shared/directives/popover'; +const popoverTitle = sprintf( + _.escape( + __( + `This pipeline makes use of a predefined CI/CD configuration enabled by %{strongStart}Auto DevOps.%{strongEnd}`, + ), + ), + { strongStart: '<b>', strongEnd: '</b>' }, + false, +); + export default { components: { UserAvatarLink, @@ -32,14 +44,14 @@ export default { trigger: 'focus', placement: 'top', title: `<div class="autodevops-title"> - This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b> + ${popoverTitle} </div>`, content: `<a class="autodevops-link" href="${this.autoDevopsHelpPath}" target="_blank" rel="noopener noreferrer nofollow"> - Learn more about Auto DevOps + ${_.escape(__('Learn more about Auto DevOps'))} </a>`, }; }, @@ -54,9 +66,9 @@ export default { <span>by</span> <user-avatar-link v-if="user" - :link-href="pipeline.user.path" - :img-src="pipeline.user.avatar_url" - :tooltip-text="pipeline.user.name" + :link-href="user.path" + :img-src="user.avatar_url" + :tooltip-text="user.name" class="js-pipeline-url-user" /> <span v-if="!user" class="js-pipeline-url-api api"> API </span> @@ -64,10 +76,10 @@ export default { <span v-if="pipeline.flags.latest" v-gl-tooltip - class="js-pipeline-url-latest badge badge-success" :title="__('Latest pipeline for this branch')" + class="js-pipeline-url-latest badge badge-success" > - latest + {{ __('latest') }} </span> <span v-if="pipeline.flags.yaml_errors" @@ -75,7 +87,7 @@ export default { :title="pipeline.yaml_errors" class="js-pipeline-url-yaml badge badge-danger" > - yaml invalid + {{ __('yaml invalid') }} </span> <span v-if="pipeline.flags.failure_reason" @@ -83,7 +95,7 @@ export default { :title="pipeline.failure_reason" class="js-pipeline-url-failure badge badge-danger" > - error + {{ __('error') }} </span> <gl-link v-if="pipeline.flags.auto_devops" @@ -95,7 +107,7 @@ export default { Auto DevOps </gl-link> <span v-if="pipeline.flags.stuck" class="js-pipeline-url-stuck badge badge-warning"> - stuck + {{ __('stuck') }} </span> <span v-if="pipeline.flags.merge_request" @@ -103,7 +115,7 @@ export default { :title="__('This pipeline is run in a merge request context')" class="js-pipeline-url-mergerequest badge badge-info" > - merge request + {{ __('merge request') }} </span> </div> </div> diff --git a/app/assets/javascripts/releases/components/release_block.vue b/app/assets/javascripts/releases/components/release_block.vue index 34b97826cdb..4295fef8f0a 100644 --- a/app/assets/javascripts/releases/components/release_block.vue +++ b/app/assets/javascripts/releases/components/release_block.vue @@ -45,7 +45,7 @@ export default { return this.release.author || {}; }, hasAuthor() { - return _.isEmpty(this.author); + return !_.isEmpty(this.author); }, }, }; diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 2f7ed4a982c..937a2847a58 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -89,7 +89,6 @@ export default { return this.referencedUsers.length >= referencedUsersThreshold; }, lineContent() { - const FIRST_CHAR_REGEX = /^(\+|-)/; const [firstSuggestion] = this.suggestions; if (firstSuggestion) { return firstSuggestion.from_content; @@ -99,7 +98,7 @@ export default { const { rich_text: richText, text } = this.line; if (text) { - return text.replace(FIRST_CHAR_REGEX, ''); + return text; } return _.unescape(stripHtml(richText).replace(/\n/g, '')); diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue index 7c6dbee3e19..721f0276ac8 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue @@ -82,13 +82,12 @@ export default { // extracts the suggested lines from the markdown // calculates a line number for each line - const FIRST_CHAR_REGEX = /^(\+|-)/; const newLines = suggestionEl.querySelectorAll('.line'); const fromLine = this.suggestions.length ? this.suggestions[0].from_line : this.fromLine; const lines = []; newLines.forEach((line, i) => { - const content = `${line.innerText.replace(FIRST_CHAR_REGEX, '')}\n`; + const content = `${line.innerText}\n`; const lineNumber = fromLine + i; lines.push({ content, lineNumber }); }); diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue new file mode 100644 index 00000000000..7361867edc5 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue @@ -0,0 +1,83 @@ +<script> +import { GlButton } from '@gitlab/ui'; +import { sprintf, __ } from '~/locale'; +import UserAvatarLink from './user_avatar_link.vue'; + +export default { + components: { + UserAvatarLink, + GlButton, + }, + props: { + items: { + type: Array, + required: true, + }, + breakpoint: { + type: Number, + required: false, + default: 10, + }, + imgSize: { + type: Number, + required: false, + default: 20, + }, + }, + data() { + return { + isExpanded: false, + }; + }, + computed: { + visibleItems() { + if (!this.hasHiddenItems) { + return this.items; + } + + return this.items.slice(0, this.breakpoint); + }, + hasHiddenItems() { + return this.hasBreakpoint && !this.isExpanded && this.items.length > this.breakpoint; + }, + hasBreakpoint() { + return this.breakpoint > 0 && this.items.length > this.breakpoint; + }, + expandText() { + if (!this.hasHiddenItems) { + return ''; + } + + const count = this.items.length - this.breakpoint; + + return sprintf(__('%{count} more'), { count }); + }, + }, + methods: { + expand() { + this.isExpanded = true; + }, + collapse() { + this.isExpanded = false; + }, + }, +}; +</script> + +<template> + <div> + <user-avatar-link + v-for="item in visibleItems" + :key="item.id" + :link-href="item.web_url" + :img-src="item.avatar_url" + :img-alt="item.name" + :tooltip-text="item.name" + :img-size="imgSize" + /> + <template v-if="hasBreakpoint"> + <gl-button v-if="hasHiddenItems" variant="link" @click="expand"> {{ expandText }} </gl-button> + <gl-button v-else variant="link" @click="collapse"> {{ __('show less') }} </gl-button> + </template> + </div> +</template> |