diff options
74 files changed, 2377 insertions, 2160 deletions
diff --git a/app/assets/javascripts/notebook/cells/markdown.vue b/app/assets/javascripts/notebook/cells/markdown.vue index 82c51a1068c..d0ec70f1fcf 100644 --- a/app/assets/javascripts/notebook/cells/markdown.vue +++ b/app/assets/javascripts/notebook/cells/markdown.vue @@ -91,18 +91,21 @@ <template> <div class="cell text-cell"> <prompt /> - <div class="markdown" v-html="markdown"></div> + <div + class="markdown" + v-html="markdown"> + </div> </div> </template> <style> -.markdown .katex { - display: block; - text-align: center; -} + .markdown .katex { + display: block; + text-align: center; + } -.markdown .inline-katex .katex { - display: inline; - text-align: initial; -} + .markdown .inline-katex .katex { + display: inline; + text-align: initial; + } </style> diff --git a/app/assets/javascripts/notebook/cells/output/html.vue b/app/assets/javascripts/notebook/cells/output/html.vue index 2110a9de7ed..ebba5954de9 100644 --- a/app/assets/javascripts/notebook/cells/output/html.vue +++ b/app/assets/javascripts/notebook/cells/output/html.vue @@ -1,17 +1,17 @@ <script> -import Prompt from '../prompt.vue'; + import Prompt from '../prompt.vue'; -export default { - props: { - rawCode: { - type: String, - required: true, + export default { + components: { + prompt: Prompt, }, - }, - components: { - prompt: Prompt, - }, -}; + props: { + rawCode: { + type: String, + required: true, + }, + }, + }; </script> <template> diff --git a/app/assets/javascripts/notebook/cells/output/image.vue b/app/assets/javascripts/notebook/cells/output/image.vue index fbb39ea6e2d..67d6c5ad12b 100644 --- a/app/assets/javascripts/notebook/cells/output/image.vue +++ b/app/assets/javascripts/notebook/cells/output/image.vue @@ -1,27 +1,26 @@ <script> -import Prompt from '../prompt.vue'; + import Prompt from '../prompt.vue'; -export default { - props: { - outputType: { - type: String, - required: true, + export default { + components: { + prompt: Prompt, }, - rawCode: { - type: String, - required: true, + props: { + outputType: { + type: String, + required: true, + }, + rawCode: { + type: String, + required: true, + }, }, - }, - components: { - prompt: Prompt, - }, -}; + }; </script> <template> <div class="output"> <prompt /> - <img - :src="'data:' + outputType + ';base64,' + rawCode" /> + <img :src="'data:' + outputType + ';base64,' + rawCode" /> </div> </template> diff --git a/app/assets/javascripts/notebook/cells/output/index.vue b/app/assets/javascripts/notebook/cells/output/index.vue index 05af0bf1e8e..5bde30799b0 100644 --- a/app/assets/javascripts/notebook/cells/output/index.vue +++ b/app/assets/javascripts/notebook/cells/output/index.vue @@ -1,83 +1,86 @@ <script> -import CodeCell from '../code/index.vue'; -import Html from './html.vue'; -import Image from './image.vue'; + import CodeCell from '../code/index.vue'; + import Html from './html.vue'; + import Image from './image.vue'; -export default { - props: { - codeCssClass: { - type: String, - required: false, - default: '', + export default { + components: { + 'code-cell': CodeCell, + 'html-output': Html, + 'image-output': Image, }, - count: { - type: Number, - required: false, - default: 0, + props: { + codeCssClass: { + type: String, + required: false, + default: '', + }, + count: { + type: Number, + required: false, + default: 0, + }, + output: { + type: Object, + requred: true, + default: () => ({}), + }, }, - output: { - type: Object, - requred: true, + data() { + return { + outputType: '', + }; }, - }, - components: { - 'code-cell': CodeCell, - 'html-output': Html, - 'image-output': Image, - }, - data() { - return { - outputType: '', - }; - }, - computed: { - componentName() { - if (this.output.text) { - return 'code-cell'; - } else if (this.output.data['image/png']) { - this.outputType = 'image/png'; + computed: { + componentName() { + if (this.output.text) { + return 'code-cell'; + } else if (this.output.data['image/png']) { + this.outputType = 'image/png'; - return 'image-output'; - } else if (this.output.data['text/html']) { - this.outputType = 'text/html'; + return 'image-output'; + } else if (this.output.data['text/html']) { + this.outputType = 'text/html'; - return 'html-output'; - } else if (this.output.data['image/svg+xml']) { - this.outputType = 'image/svg+xml'; + return 'html-output'; + } else if (this.output.data['image/svg+xml']) { + this.outputType = 'image/svg+xml'; - return 'html-output'; - } + return 'html-output'; + } - this.outputType = 'text/plain'; - return 'code-cell'; - }, - rawCode() { - if (this.output.text) { - return this.output.text.join(''); - } + this.outputType = 'text/plain'; + return 'code-cell'; + }, + rawCode() { + if (this.output.text) { + return this.output.text.join(''); + } - return this.dataForType(this.outputType); + return this.dataForType(this.outputType); + }, }, - }, - methods: { - dataForType(type) { - let data = this.output.data[type]; + methods: { + dataForType(type) { + let data = this.output.data[type]; - if (typeof data === 'object') { - data = data.join(''); - } + if (typeof data === 'object') { + data = data.join(''); + } - return data; + return data; + }, }, - }, -}; + }; </script> <template> - <component :is="componentName" + <component + :is="componentName" type="output" - :outputType="outputType" + :output-type="outputType" :count="count" :raw-code="rawCode" - :code-css-class="codeCssClass" /> + :code-css-class="codeCssClass" + /> </template> diff --git a/app/assets/javascripts/notebook/cells/prompt.vue b/app/assets/javascripts/notebook/cells/prompt.vue index 039fb99293d..fe1fc37e1dc 100644 --- a/app/assets/javascripts/notebook/cells/prompt.vue +++ b/app/assets/javascripts/notebook/cells/prompt.vue @@ -4,10 +4,17 @@ type: { type: String, required: false, + default: '', }, count: { type: Number, required: false, + default: 0, + }, + }, + computed: { + hasKeys() { + return this.type !== '' && this.count; }, }, }; @@ -15,16 +22,16 @@ <template> <div class="prompt"> - <span v-if="type && count"> + <span v-if="hasKeys"> {{ type }} [{{ count }}]: </span> </div> </template> <style scoped> -.prompt { - padding: 0 10px; - min-width: 7em; - font-family: monospace; -} + .prompt { + padding: 0 10px; + min-width: 7em; + font-family: monospace; + } </style> diff --git a/app/assets/javascripts/notebook/index.vue b/app/assets/javascripts/notebook/index.vue index e88806431af..e2e3b08c77f 100644 --- a/app/assets/javascripts/notebook/index.vue +++ b/app/assets/javascripts/notebook/index.vue @@ -20,11 +20,6 @@ default: '', }, }, - methods: { - cellType(type) { - return `${type}-cell`; - }, - }, computed: { cells() { if (this.notebook.worksheets) { @@ -45,6 +40,11 @@ return Object.keys(this.notebook).length; }, }, + methods: { + cellType(type) { + return `${type}-cell`; + }, + }, }; </script> diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 778db2f7132..d97c5b6d492 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -15,7 +15,17 @@ import issuableStateMixin from '../mixins/issuable_state'; export default { - name: 'commentForm', + name: 'CommentForm', + components: { + issueWarning, + noteSignedOutWidget, + discussionLockedWidget, + markdownField, + userAvatarLink, + }, + mixins: [ + issuableStateMixin, + ], data() { return { note: '', @@ -27,21 +37,6 @@ isSubmitButtonDisabled: true, }; }, - components: { - issueWarning, - noteSignedOutWidget, - discussionLockedWidget, - markdownField, - userAvatarLink, - }, - watch: { - note(newNote) { - this.setIsSubmitButtonDisabled(newNote, this.isSubmitting); - }, - isSubmitting(newValue) { - this.setIsSubmitButtonDisabled(this.note, newValue); - }, - }, computed: { ...mapGetters([ 'getCurrentUserLastNote', @@ -99,6 +94,23 @@ return this.getNoteableData.create_note_path; }, }, + watch: { + note(newNote) { + this.setIsSubmitButtonDisabled(newNote, this.isSubmitting); + }, + isSubmitting(newValue) { + this.setIsSubmitButtonDisabled(this.note, newValue); + }, + }, + mounted() { + // jQuery is needed here because it is a custom event being dispatched with jQuery. + $(document).on('issuable:change', (e, isClosed) => { + this.issueState = isClosed ? constants.CLOSED : constants.REOPENED; + }); + + this.initAutoSave(); + this.initTaskList(); + }, methods: { ...mapActions([ 'saveNote', @@ -231,18 +243,6 @@ Please check your network connection and try again.`; }); }, }, - mixins: [ - issuableStateMixin, - ], - mounted() { - // jQuery is needed here because it is a custom event being dispatched with jQuery. - $(document).on('issuable:change', (e, isClosed) => { - this.issueState = isClosed ? constants.CLOSED : constants.REOPENED; - }); - - this.initAutoSave(); - this.initTaskList(); - }, }; </script> @@ -266,7 +266,7 @@ Please check your network connection and try again.`; :img-src="author.avatar_url" :img-alt="author.name" :img-size="40" - /> + /> </div> <div class="timeline-content timeline-content-form"> <form @@ -310,7 +310,7 @@ Please check your network connection and try again.`; :disabled="isSubmitButtonDisabled" class="btn btn-create comment-btn js-comment-button js-comment-submit-button" type="submit"> - {{commentButtonTitle}} + {{ commentButtonTitle }} </button> <button :disabled="isSubmitButtonDisabled" @@ -352,7 +352,7 @@ Please check your network connection and try again.`; <i aria-hidden="true" class="fa fa-check icon"> - </i> + </i> <div class="description"> <strong>Start discussion</strong> <p> @@ -370,7 +370,7 @@ Please check your network connection and try again.`; :class="actionButtonClassNames" :disabled="isSubmitting" class="btn btn-comment btn-comment-and-close js-action-button"> - {{issueActionButtonTitle}} + {{ issueActionButtonTitle }} </button> <button type="button" diff --git a/app/assets/javascripts/notes/components/discussion_locked_widget.vue b/app/assets/javascripts/notes/components/discussion_locked_widget.vue index e6f7ee56ff3..fc0722042cc 100644 --- a/app/assets/javascripts/notes/components/discussion_locked_widget.vue +++ b/app/assets/javascripts/notes/components/discussion_locked_widget.vue @@ -3,12 +3,12 @@ import Issuable from '~/vue_shared/mixins/issuable'; export default { - mixins: [ - Issuable, - ], components: { Icon, }, + mixins: [ + Issuable, + ], }; </script> @@ -18,9 +18,11 @@ <icon name="lock" :size="16" - class="icon"> - </icon> - <span>This {{ issuableDisplayName }} is locked. Only <b>project members</b> can comment.</span> - </span> + class="icon" + /> + <span> + This {{ issuableDisplayName }} is locked. Only <b>project members</b> can comment. + </span> + </span> </div> </template> diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue index 7fb45ed4d4b..b96d4db8848 100644 --- a/app/assets/javascripts/notes/components/note_actions.vue +++ b/app/assets/javascripts/notes/components/note_actions.vue @@ -9,7 +9,13 @@ import tooltip from '~/vue_shared/directives/tooltip'; export default { - name: 'noteActions', + name: 'NoteActions', + directives: { + tooltip, + }, + components: { + loadingIcon, + }, props: { authorId: { type: Number, @@ -41,12 +47,6 @@ required: true, }, }, - directives: { - tooltip, - }, - components: { - loadingIcon, - }, computed: { ...mapGetters([ 'getUserDataByProp', @@ -98,20 +98,21 @@ data-placement="bottom" data-container="body" href="#" - title="Add reaction"> - <loading-icon :inline="true" /> - <span - v-html="emojiSmiling" - class="link-highlight award-control-icon-neutral"> - </span> - <span - v-html="emojiSmiley" - class="link-highlight award-control-icon-positive"> - </span> - <span - v-html="emojiSmile" - class="link-highlight award-control-icon-super-positive"> - </span> + title="Add reaction" + > + <loading-icon :inline="true" /> + <span + v-html="emojiSmiling" + class="link-highlight award-control-icon-neutral"> + </span> + <span + v-html="emojiSmiley" + class="link-highlight award-control-icon-positive"> + </span> + <span + v-html="emojiSmile" + class="link-highlight award-control-icon-super-positive"> + </span> </a> </div> <div @@ -127,7 +128,8 @@ data-placement="bottom"> <span v-html="editSvg" - class="link-highlight"></span> + class="link-highlight"> + </span> </button> </div> <div @@ -143,7 +145,8 @@ data-placement="bottom"> <span class="icon" - v-html="ellipsisSvg"></span> + v-html="ellipsisSvg"> + </span> </button> <ul class="dropdown-menu more-actions-dropdown dropdown-open-left"> <li v-if="canReportAsAbuse"> diff --git a/app/assets/javascripts/notes/components/note_attachment.vue b/app/assets/javascripts/notes/components/note_attachment.vue index cd9571a4002..618b807b9cc 100644 --- a/app/assets/javascripts/notes/components/note_attachment.vue +++ b/app/assets/javascripts/notes/components/note_attachment.vue @@ -1,6 +1,6 @@ <script> export default { - name: 'noteAttachment', + name: 'NoteAttachment', props: { attachment: { type: Object, @@ -19,7 +19,8 @@ rel="noopener noreferrer"> <img :src="attachment.url" - class="note-image-attach" /> + class="note-image-attach" + /> </a> <div class="attachment"> <a @@ -29,8 +30,9 @@ rel="noopener noreferrer"> <i class="fa fa-paperclip" - aria-hidden="true"></i> - {{attachment.filename}} + aria-hidden="true"> + </i> + {{ attachment.filename }} </a> </div> </div> diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue index c3a340139e7..caa9701e03f 100644 --- a/app/assets/javascripts/notes/components/note_awards_list.vue +++ b/app/assets/javascripts/notes/components/note_awards_list.vue @@ -8,6 +8,9 @@ import tooltip from '../../vue_shared/directives/tooltip'; export default { + directives: { + tooltip, + }, props: { awards: { type: Array, @@ -26,9 +29,6 @@ required: true, }, }, - directives: { - tooltip, - }, computed: { ...mapGetters([ 'getUserData', @@ -73,6 +73,11 @@ return this.getUserData.id; }, }, + created() { + this.emojiSmiling = emojiSmiling; + this.emojiSmile = emojiSmile; + this.emojiSmiley = emojiSmiley; + }, methods: { ...mapActions([ 'toggleAwardRequest', @@ -168,11 +173,6 @@ .catch(() => Flash('Something went wrong on our end.')); }, }, - created() { - this.emojiSmiling = emojiSmiling; - this.emojiSmile = emojiSmile; - this.emojiSmiley = emojiSmiley; - }, }; </script> @@ -191,7 +191,7 @@ type="button"> <span v-html="getAwardHTML(awardName)"></span> <span class="award-control-text js-counter"> - {{awardList.length}} + {{ awardList.length }} </span> </button> <div diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue index ac4e1ffe53a..2d7cd30115d 100644 --- a/app/assets/javascripts/notes/components/note_body.vue +++ b/app/assets/javascripts/notes/components/note_body.vue @@ -7,6 +7,15 @@ import autosave from '../mixins/autosave'; export default { + components: { + noteEditedText, + noteAwardsList, + noteAttachment, + noteForm, + }, + mixins: [ + autosave, + ], props: { note: { type: Object, @@ -22,40 +31,11 @@ default: false, }, }, - mixins: [ - autosave, - ], - components: { - noteEditedText, - noteAwardsList, - noteAttachment, - noteForm, - }, computed: { noteBody() { return this.note.note; }, }, - methods: { - renderGFM() { - $(this.$refs['note-body']).renderGFM(); - }, - initTaskList() { - if (this.canEdit) { - this.taskList = new TaskList({ - dataType: 'note', - fieldName: 'note', - selector: '.notes', - }); - } - }, - handleFormUpdate(note, parentElement, callback) { - this.$emit('handleFormUpdate', note, parentElement, callback); - }, - formCancelHandler(shouldConfirm, isDirty) { - this.$emit('cancelFormEdition', shouldConfirm, isDirty); - }, - }, mounted() { this.renderGFM(); this.initTaskList(); @@ -76,6 +56,26 @@ } } }, + methods: { + renderGFM() { + $(this.$refs['note-body']).renderGFM(); + }, + initTaskList() { + if (this.canEdit) { + this.taskList = new TaskList({ + dataType: 'note', + fieldName: 'note', + selector: '.notes', + }); + } + }, + handleFormUpdate(note, parentElement, callback) { + this.$emit('handleFormUpdate', note, parentElement, callback); + }, + formCancelHandler(shouldConfirm, isDirty) { + this.$emit('cancelFormEdition', shouldConfirm, isDirty); + }, + }, }; </script> @@ -95,7 +95,7 @@ :is-editing="isEditing" :note-body="noteBody" :note-id="note.id" - /> + /> <textarea v-if="canEdit" v-model="note.note" @@ -106,17 +106,17 @@ :edited-at="note.last_edited_at" :edited-by="note.last_edited_by" action-text="Edited" - /> + /> <note-awards-list v-if="note.award_emoji.length" :note-id="note.id" :note-author-id="note.author.id" :awards="note.award_emoji" :toggle-award-path="note.toggle_award_path" - /> + /> <note-attachment v-if="note.attachment" :attachment="note.attachment" - /> + /> </div> </template> diff --git a/app/assets/javascripts/notes/components/note_edited_text.vue b/app/assets/javascripts/notes/components/note_edited_text.vue index 49e09f0ecc5..ae2e52554d2 100644 --- a/app/assets/javascripts/notes/components/note_edited_text.vue +++ b/app/assets/javascripts/notes/components/note_edited_text.vue @@ -2,7 +2,10 @@ import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue'; export default { - name: 'editedNoteText', + name: 'EditedNoteText', + components: { + timeAgoTooltip, + }, props: { actionText: { type: String, @@ -15,6 +18,7 @@ editedBy: { type: Object, required: false, + default: () => ({}), }, className: { type: String, @@ -22,25 +26,22 @@ default: 'edited-text', }, }, - components: { - timeAgoTooltip, - }, }; </script> <template> <div :class="className"> - {{actionText}} + {{ actionText }} <time-ago-tooltip :time="editedAt" tooltip-placement="bottom" - /> + /> <template v-if="editedBy"> by <a :href="editedBy.path" class="js-vue-author author_link"> - {{editedBy.name}} + {{ editedBy.name }} </a> </template> </div> diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index 4d527cb6643..53042509aff 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -6,7 +6,14 @@ import issuableStateMixin from '../mixins/issuable_state'; export default { - name: 'issueNoteForm', + name: 'IssueNoteForm', + components: { + issueWarning, + markdownField, + }, + mixins: [ + issuableStateMixin, + ], props: { noteBody: { type: String, @@ -16,6 +23,7 @@ noteId: { type: Number, required: false, + default: 0, }, saveButtonTitle: { type: String, @@ -39,10 +47,6 @@ isSubmitting: false, }; }, - components: { - issueWarning, - markdownField, - }, computed: { ...mapGetters([ 'getDiscussionLastNote', @@ -70,6 +74,18 @@ return !this.note.length || this.isSubmitting; }, }, + watch: { + noteBody() { + if (this.note === this.noteBody) { + this.note = this.noteBody; + } else { + this.conflictWhileEditing = true; + } + }, + }, + mounted() { + this.$refs.textarea.focus(); + }, methods: { handleUpdate() { this.isSubmitting = true; @@ -94,26 +110,13 @@ this.$emit('cancelFormEdition', shouldConfirm, this.noteBody !== this.note); }, }, - mixins: [ - issuableStateMixin, - ], - mounted() { - this.$refs.textarea.focus(); - }, - watch: { - noteBody() { - if (this.note === this.noteBody) { - this.note = this.noteBody; - } else { - this.conflictWhileEditing = true; - } - }, - }, }; </script> <template> - <div ref="editNoteForm" class="note-edit-form current-note-edit-form"> + <div + ref="editNoteForm" + class="note-edit-form current-note-edit-form"> <div v-if="conflictWhileEditing" class="js-conflict-edit-warning alert alert-danger"> @@ -121,12 +124,13 @@ <a :href="noteHash" target="_blank" - rel="noopener noreferrer">updated comment</a> - to ensure information is not lost. + rel="noopener noreferrer"> + updated comment + </a> + to ensure information is not lost. </div> <div class="flash-container timeline-content"></div> - <form - class="edit-note common-note-form js-quick-submit gfm-form"> + <form class="edit-note common-note-form js-quick-submit gfm-form"> <issue-warning v-if="hasWarning(getNoteableData)" @@ -160,7 +164,7 @@ @click="handleUpdate()" :disabled="isDisabled" class="js-vue-issue-save btn btn-save"> - {{saveButtonTitle}} + {{ saveButtonTitle }} </button> <button @click="cancelHandler()" diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue index 63aa3d777d0..b28dda4904d 100644 --- a/app/assets/javascripts/notes/components/note_header.vue +++ b/app/assets/javascripts/notes/components/note_header.vue @@ -3,6 +3,9 @@ import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue'; export default { + components: { + timeAgoTooltip, + }, props: { author: { type: Object, @@ -37,9 +40,6 @@ isExpanded: true, }; }, - components: { - timeAgoTooltip, - }, computed: { toggleChevronClass() { return this.isExpanded ? 'fa-chevron-up' : 'fa-chevron-down'; @@ -67,16 +67,16 @@ <div class="note-header-info"> <a :href="author.path"> <span class="note-header-author-name"> - {{author.name}} + {{ author.name }} </span> <span class="note-headline-light"> - @{{author.username}} + @{{ author.username }} </span> </a> <span class="note-headline-light"> <span class="note-headline-meta"> <template v-if="actionText"> - {{actionText}} + {{ actionText }} </template> <span v-if="actionTextHtml" @@ -90,12 +90,13 @@ <time-ago-tooltip :time="createdAt" tooltip-placement="bottom" - /> + /> </a> <i class="fa fa-spinner fa-spin editing-spinner" aria-label="Comment is being updated" - aria-hidden="true"> + aria-hidden="true" + > </i> </span> </span> @@ -106,12 +107,12 @@ @click="handleToggle" class="note-action-button discussion-toggle-button js-vue-toggle-button" type="button"> - <i - :class="toggleChevronClass" - class="fa" - aria-hidden="true"> - </i> - Toggle discussion + <i + :class="toggleChevronClass" + class="fa" + aria-hidden="true"> + </i> + Toggle discussion </button> </div> </div> diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 873c4f1ff96..98a06c5fc71 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -13,17 +13,6 @@ import autosave from '../mixins/autosave'; export default { - props: { - note: { - type: Object, - required: true, - }, - }, - data() { - return { - isReplying: false, - }; - }, components: { noteableNote, userAvatarLink, @@ -37,6 +26,17 @@ mixins: [ autosave, ], + props: { + note: { + type: Object, + required: true, + }, + }, + data() { + return { + isReplying: false, + }; + }, computed: { ...mapGetters([ 'getNoteableData', @@ -72,6 +72,20 @@ return null; }, }, + mounted() { + if (this.isReplying) { + this.initAutoSave(); + } + }, + updated() { + if (this.isReplying) { + if (!this.autosave) { + this.initAutoSave(); + } else { + this.setAutoSave(); + } + } + }, methods: { ...mapActions([ 'saveNote', @@ -139,20 +153,6 @@ Please check your network connection and try again.`; }); }, }, - mounted() { - if (this.isReplying) { - this.initAutoSave(); - } - }, - updated() { - if (this.isReplying) { - if (!this.autosave) { - this.initAutoSave(); - } else { - this.setAutoSave(); - } - } - }, }; </script> @@ -165,7 +165,7 @@ Please check your network connection and try again.`; :img-src="author.avatar_url" :img-alt="author.name" :img-size="40" - /> + /> </div> <div class="timeline-content"> <div class="discussion"> @@ -185,42 +185,43 @@ Please check your network connection and try again.`; :edited-by="lastUpdatedBy" action-text="Last updated" class-name="discussion-headline-light js-discussion-headline" - /> - </div> + /> </div> - <div - v-if="note.expanded" - class="discussion-body"> - <div class="panel panel-default"> - <div class="discussion-notes"> - <ul class="notes"> - <component - v-for="note in note.notes" - :is="componentName(note)" - :note="componentData(note)" - :key="note.id" - /> - </ul> - <div - :class="{ 'is-replying': isReplying }" - class="discussion-reply-holder"> - <button - v-if="canReply && !isReplying" - @click="showReplyForm" - type="button" - class="js-vue-discussion-reply btn btn-text-field" - title="Add a reply">Reply...</button> - <note-form - v-if="isReplying" - save-button-title="Comment" - :discussion="note" - :is-editing="false" - @handleFormUpdate="saveReply" - @cancelFormEdition="cancelReplyForm" - ref="noteForm" - /> - <note-signed-out-widget v-if="!canReply" /> - </div> + </div> + <div + v-if="note.expanded" + class="discussion-body"> + <div class="panel panel-default"> + <div class="discussion-notes"> + <ul class="notes"> + <component + v-for="note in note.notes" + :is="componentName(note)" + :note="componentData(note)" + :key="note.id" + /> + </ul> + <div + :class="{ 'is-replying': isReplying }" + class="discussion-reply-holder"> + <button + v-if="canReply && !isReplying" + @click="showReplyForm" + type="button" + class="js-vue-discussion-reply btn btn-text-field" + title="Add a reply"> + Reply... + </button> + <note-form + v-if="isReplying" + save-button-title="Comment" + :discussion="note" + :is-editing="false" + @handleFormUpdate="saveReply" + @cancelFormEdition="cancelReplyForm" + ref="noteForm" + /> + <note-signed-out-widget v-if="!canReply" /> </div> </div> </div> diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index 9186d6ff64a..30e7ccc8229 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -9,6 +9,12 @@ import eventHub from '../event_hub'; export default { + components: { + userAvatarLink, + noteHeader, + noteActions, + noteBody, + }, props: { note: { type: Object, @@ -22,12 +28,6 @@ isRequesting: false, }; }, - components: { - userAvatarLink, - noteHeader, - noteActions, - noteBody, - }, computed: { ...mapGetters([ 'targetNoteHash', @@ -51,6 +51,16 @@ return `note_${this.note.id}`; }, }, + + created() { + eventHub.$on('enterEditMode', ({ noteId }) => { + if (noteId === this.note.id) { + this.isEditing = true; + this.scrollToNoteIfNeeded($(this.$el)); + } + }); + }, + methods: { ...mapActions([ 'deleteNote', @@ -126,14 +136,6 @@ this.$refs.noteBody.$refs.noteForm.note = noteText; }, }, - created() { - eventHub.$on('enterEditMode', ({ noteId }) => { - if (noteId === this.note.id) { - this.isEditing = true; - this.scrollToNoteIfNeeded($(this.$el)); - } - }); - }, }; </script> @@ -150,7 +152,7 @@ :img-src="author.avatar_url" :img-alt="author.name" :img-size="40" - /> + /> </div> <div class="timeline-content"> <div class="note-header"> @@ -159,7 +161,7 @@ :created-at="note.created_at" :note-id="note.id" action-text="commented" - /> + /> <note-actions :author-id="author.id" :note-id="note.id" @@ -170,7 +172,7 @@ :report-abuse-path="note.report_abuse_path" @handleEdit="editHandler" @handleDelete="deleteHandler" - /> + /> </div> <note-body :note="note" @@ -179,7 +181,7 @@ @handleFormUpdate="formUpdateHandler" @cancelFormEdition="formCancelHandler" ref="noteBody" - /> + /> </div> </div> </li> diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index c4cae4b3b6f..92db4830704 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -13,7 +13,16 @@ import loadingIcon from '../../vue_shared/components/loading_icon.vue'; export default { - name: 'notesApp', + name: 'NotesApp', + components: { + noteableNote, + noteableDiscussion, + systemNote, + commentForm, + loadingIcon, + placeholderNote, + placeholderSystemNote, + }, props: { noteableData: { type: Object, @@ -26,7 +35,7 @@ userData: { type: Object, required: false, - default: {}, + default: () => ({}), }, }, store, @@ -35,21 +44,30 @@ isLoading: true, }; }, - components: { - noteableNote, - noteableDiscussion, - systemNote, - commentForm, - loadingIcon, - placeholderNote, - placeholderSystemNote, - }, computed: { ...mapGetters([ 'notes', 'getNotesDataByProp', ]), }, + created() { + this.setNotesData(this.notesData); + this.setNoteableData(this.noteableData); + this.setUserData(this.userData); + }, + mounted() { + this.fetchNotes(); + + const parentElement = this.$el.parentElement; + + if (parentElement && + parentElement.classList.contains('js-vue-notes-event')) { + parentElement.addEventListener('toggleAward', (event) => { + const { awardName, noteId } = event.detail; + this.actionToggleAward({ awardName, noteId }); + }); + } + }, methods: { ...mapActions({ actionFetchNotes: 'fetchNotes', @@ -105,24 +123,6 @@ } }, }, - created() { - this.setNotesData(this.notesData); - this.setNoteableData(this.noteableData); - this.setUserData(this.userData); - }, - mounted() { - this.fetchNotes(); - - const parentElement = this.$el.parentElement; - - if (parentElement && - parentElement.classList.contains('js-vue-notes-event')) { - parentElement.addEventListener('toggleAward', (event) => { - const { awardName, noteId } = event.detail; - this.actionToggleAward({ awardName, noteId }); - }); - } - }, }; </script> @@ -144,7 +144,7 @@ :is="getComponentName(note)" :note="getComponentData(note)" :key="note.id" - /> + /> </ul> <comment-form /> diff --git a/app/assets/javascripts/pdf/index.vue b/app/assets/javascripts/pdf/index.vue index c8a2f778ee8..00f32d9de78 100644 --- a/app/assets/javascripts/pdf/index.vue +++ b/app/assets/javascripts/pdf/index.vue @@ -5,6 +5,7 @@ import page from './page/index.vue'; export default { + components: { page }, props: { pdf: { type: [String, Uint8Array], @@ -17,8 +18,6 @@ pages: [], }; }, - components: { page }, - watch: { pdf: 'load' }, computed: { document() { return typeof this.pdf === 'string' ? this.pdf : { data: this.pdf }; @@ -27,6 +26,11 @@ return this.pdf && this.pdf.length > 0; }, }, + watch: { pdf: 'load' }, + mounted() { + pdfjsLib.PDFJS.workerSrc = workerSrc; + if (this.hasPDF) this.load(); + }, methods: { load() { this.pages = []; @@ -47,20 +51,20 @@ return Promise.all(pagePromises); }, }, - mounted() { - pdfjsLib.PDFJS.workerSrc = workerSrc; - if (this.hasPDF) this.load(); - }, }; </script> <template> - <div class="pdf-viewer" v-if="hasPDF"> - <page v-for="(page, index) in pages" + <div + class="pdf-viewer" + v-if="hasPDF"> + <page + v-for="(page, index) in pages" :key="index" :v-if="!loading" :page="page" - :number="index + 1" /> + :number="index + 1" + /> </div> </template> diff --git a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue b/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue index b5d85299cf8..2d18fa2044b 100644 --- a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue +++ b/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue @@ -32,6 +32,20 @@ return !!(this.customInputEnabled || !this.intervalIsPreset); }, }, + watch: { + cronInterval() { + // updates field validation state when model changes, as + // glFieldError only updates on input. + this.$nextTick(() => { + gl.pipelineScheduleFieldErrors.updateFormValidityState(); + }); + }, + }, + created() { + if (this.intervalIsPreset) { + this.enableCustomInput = false; + } + }, methods: { toggleCustomInput(shouldEnable) { this.customInputEnabled = shouldEnable; @@ -43,20 +57,6 @@ } }, }, - created() { - if (this.intervalIsPreset) { - this.enableCustomInput = false; - } - }, - watch: { - cronInterval() { - // updates field validation state when model changes, as - // glFieldError only updates on input. - this.$nextTick(() => { - gl.pipelineScheduleFieldErrors.updateFormValidityState(); - }); - }, - }, }; </script> @@ -78,7 +78,12 @@ </label> <span class="cron-syntax-link-wrap"> - (<a :href="cronSyntaxUrl" target="_blank">{{ __('Cron syntax') }}</a>) + (<a + :href="cronSyntaxUrl" + target="_blank" + > + {{ __('Cron syntax') }} + </a>) </span> </div> @@ -93,7 +98,10 @@ @click="toggleCustomInput(false)" /> - <label class="label-light" for="every-day"> + <label + class="label-light" + for="every-day" + > {{ __('Every day (at 4:00am)') }} </label> </div> @@ -109,7 +117,10 @@ @click="toggleCustomInput(false)" /> - <label class="label-light" for="every-week"> + <label + class="label-light" + for="every-week" + > {{ __('Every week (Sundays at 4:00am)') }} </label> </div> @@ -125,7 +136,10 @@ @click="toggleCustomInput(false)" /> - <label class="label-light" for="every-month"> + <label + class="label-light" + for="every-month" + > {{ __('Every month (on the 1st at 4:00am)') }} </label> </div> diff --git a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue index 6e0bc2d697a..e4dafce2b15 100644 --- a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue +++ b/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue @@ -16,15 +16,15 @@ calloutDismissed: Cookies.get(cookieKey) === 'true', }; }, + created() { + this.illustrationSvg = illustrationSvg; + }, methods: { dismissCallout() { this.calloutDismissed = true; Cookies.set(cookieKey, this.calloutDismissed, { expires: 365 }); }, }, - created() { - this.illustrationSvg = illustrationSvg; - }, }; </script> <template> @@ -41,17 +41,23 @@ class="fa fa-times"> </i> </button> - <div class="svg-container" v-html="illustrationSvg"></div> + <div + class="svg-container" + v-html="illustrationSvg"> + </div> <div class="user-callout-copy"> <h4>{{ __('Scheduling Pipelines') }}</h4> <p> - {{ __('The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.') }} + {{ __('The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.') }} </p> <p> {{ __('Learn more in the') }} <a :href="docsUrl" target="_blank" - rel="nofollow">{{ s__('Learn more in the|pipeline schedules documentation') }}</a>. <!-- oneline to prevent extra space before period --> + rel="nofollow" + > + {{ s__('Learn more in the|pipeline schedules documentation') }}</a>. + <!-- oneline to prevent extra space before period --> </p> </div> </div> diff --git a/app/assets/javascripts/pipelines/components/async_button.vue b/app/assets/javascripts/pipelines/components/async_button.vue index 16cc0761fc1..4ad3f66ee8c 100644 --- a/app/assets/javascripts/pipelines/components/async_button.vue +++ b/app/assets/javascripts/pipelines/components/async_button.vue @@ -1,67 +1,68 @@ <script> -/* eslint-disable no-new, no-alert */ + /* eslint-disable no-alert */ -import eventHub from '../event_hub'; -import loadingIcon from '../../vue_shared/components/loading_icon.vue'; -import tooltip from '../../vue_shared/directives/tooltip'; + import eventHub from '../event_hub'; + import loadingIcon from '../../vue_shared/components/loading_icon.vue'; + import tooltip from '../../vue_shared/directives/tooltip'; -export default { - props: { - endpoint: { - type: String, - required: true, + export default { + directives: { + tooltip, }, - title: { - type: String, - required: true, + components: { + loadingIcon, }, - icon: { - type: String, - required: true, + props: { + endpoint: { + type: String, + required: true, + }, + title: { + type: String, + required: true, + }, + icon: { + type: String, + required: true, + }, + cssClass: { + type: String, + required: true, + }, + confirmActionMessage: { + type: String, + required: false, + default: '', + }, }, - cssClass: { - type: String, - required: true, + data() { + return { + isLoading: false, + }; }, - confirmActionMessage: { - type: String, - required: false, + computed: { + iconClass() { + return `fa fa-${this.icon}`; + }, + buttonClass() { + return `btn ${this.cssClass}`; + }, }, - }, - directives: { - tooltip, - }, - components: { - loadingIcon, - }, - data() { - return { - isLoading: false, - }; - }, - computed: { - iconClass() { - return `fa fa-${this.icon}`; - }, - buttonClass() { - return `btn ${this.cssClass}`; - }, - }, - methods: { - onClick() { - if (this.confirmActionMessage && confirm(this.confirmActionMessage)) { - this.makeRequest(); - } else if (!this.confirmActionMessage) { - this.makeRequest(); - } - }, - makeRequest() { - this.isLoading = true; + methods: { + onClick() { + if (this.confirmActionMessage !== '' && confirm(this.confirmActionMessage)) { + this.makeRequest(); + } else if (this.confirmActionMessage === '') { + this.makeRequest(); + } + }, + makeRequest() { + this.isLoading = true; - eventHub.$emit('postAction', this.endpoint); + eventHub.$emit('postAction', this.endpoint); + }, }, - }, -}; + }; </script> <template> diff --git a/app/assets/javascripts/pipelines/components/empty_state.vue b/app/assets/javascripts/pipelines/components/empty_state.vue index 78322f30685..b551609116a 100644 --- a/app/assets/javascripts/pipelines/components/empty_state.vue +++ b/app/assets/javascripts/pipelines/components/empty_state.vue @@ -32,7 +32,7 @@ <a :href="helpPagePath" class="btn btn-info" - > + > {{ s__("Pipelines|Get started with Pipelines") }} </a> </div> diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue index 19d8e1f49cf..d7effb27bff 100644 --- a/app/assets/javascripts/pipelines/components/graph/action_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue @@ -7,6 +7,14 @@ * TODO: Remove UJS from here and use an async request instead. */ export default { + components: { + icon, + }, + + directives: { + tooltip, + }, + props: { tooltipText: { type: String, @@ -29,14 +37,6 @@ }, }, - components: { - icon, - }, - - directives: { - tooltip, - }, - computed: { cssClass() { const actionIconDash = dasherize(this.actionIcon); @@ -53,7 +53,8 @@ :href="link" class="ci-action-icon-container ci-action-icon-wrapper" :class="cssClass" - data-container="body"> - <icon :name="actionIcon"/> + data-container="body" + > + <icon :name="actionIcon" /> </a> </template> diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_action_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_action_component.vue index 1c0944d45fc..7c4fd65e36f 100644 --- a/app/assets/javascripts/pipelines/components/graph/dropdown_action_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/dropdown_action_component.vue @@ -7,6 +7,13 @@ * TODO: Remove UJS from here and use an async request instead. */ export default { + components: { + icon, + }, + + directives: { + tooltip, + }, props: { tooltipText: { type: String, @@ -28,14 +35,6 @@ required: true, }, }, - - components: { - icon, - }, - - directives: { - tooltip, - }, }; </script> <template> @@ -47,7 +46,8 @@ rel="nofollow" class="ci-action-icon-wrapper js-ci-status-icon" data-container="body" - aria-label="Job's action"> - <icon :name="actionIcon"/> + aria-label="Job's action" + > + <icon :name="actionIcon" /> </a> </template> diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue index 933d21bf17b..b86e95f0b4a 100644 --- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue @@ -27,13 +27,6 @@ * } */ export default { - props: { - job: { - type: Object, - required: true, - }, - }, - directives: { tooltip, }, @@ -43,12 +36,23 @@ jobNameComponent, }, + props: { + job: { + type: Object, + required: true, + }, + }, + computed: { tooltipText() { return `${this.job.name} - ${this.job.status.label}`; }, }, + mounted() { + this.stopDropdownClickPropagation(); + }, + methods: { /** * When the user right clicks or cmd/ctrl + click in the job name @@ -66,10 +70,6 @@ }); }, }, - - mounted() { - this.stopDropdownClickPropagation(); - }, }; </script> <template> @@ -84,22 +84,25 @@ <job-name-component :name="job.name" - :status="job.status" /> + :status="job.status" + /> <span class="dropdown-counter-badge"> - {{job.size}} + {{ job.size }} </span> </button> <ul class="dropdown-menu big-pipeline-graph-dropdown-menu js-grouped-pipeline-dropdown"> <li class="scrollable-menu"> <ul> - <li v-for="item in job.jobs"> + <li + v-for="(item, i) in job.jobs" + :key="i"> <job-component :job="item" :is-dropdown="true" css-class-job-name="mini-pipeline-graph-dropdown-item" - /> + /> </li> </ul> </li> diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue index 66bc1d1979c..a1f58580318 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue @@ -1,9 +1,13 @@ <script> import loadingIcon from '~/vue_shared/components/loading_icon.vue'; - import '~/flash'; import stageColumnComponent from './stage_column_component.vue'; export default { + components: { + stageColumnComponent, + loadingIcon, + }, + props: { isLoading: { type: Boolean, @@ -15,11 +19,6 @@ }, }, - components: { - stageColumnComponent, - loadingIcon, - }, - computed: { graph() { return this.pipeline.details && this.pipeline.details.stages; @@ -58,7 +57,7 @@ <loading-icon v-if="isLoading" size="3" - /> + /> </div> <ul @@ -70,7 +69,8 @@ :jobs="stage.groups" :key="stage.name" :stage-connector-class="stageConnectorClass(index, stage)" - :is-first-column="isFirstColumn(index)"/> + :is-first-column="isFirstColumn(index)" + /> </ul> </div> </div> diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue index b01c799643c..9b136573135 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue @@ -29,6 +29,15 @@ */ export default { + components: { + actionComponent, + dropdownActionComponent, + jobNameComponent, + }, + + directives: { + tooltip, + }, props: { job: { type: Object, @@ -48,16 +57,6 @@ }, }, - components: { - actionComponent, - dropdownActionComponent, - jobNameComponent, - }, - - directives: { - tooltip, - }, - computed: { status() { return this.job && this.job.status ? this.job.status : {}; @@ -102,12 +101,12 @@ :class="cssClassJobName" data-container="body" class="js-pipeline-graph-job-link" - > + > <job-name-component :name="job.name" :status="job.status" - /> + /> </a> <div @@ -117,12 +116,12 @@ :title="tooltipText" :class="cssClassJobName" data-container="body" - > + > <job-name-component :name="job.name" :status="job.status" - /> + /> </div> <action-component @@ -131,7 +130,7 @@ :link="status.action.path" :action-icon="status.action.icon" :action-method="status.action.method" - /> + /> <dropdown-action-component v-if="hasAction && isDropdown" @@ -139,6 +138,6 @@ :link="status.action.path" :action-icon="status.action.icon" :action-method="status.action.method" - /> + /> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue index f46d21bd6d7..14f4964a406 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue @@ -8,6 +8,9 @@ * - Dropdown badge components */ export default { + components: { + ciIcon, + }, props: { name: { type: String, @@ -19,19 +22,14 @@ required: true, }, }, - - components: { - ciIcon, - }, }; </script> <template> <span class="ci-job-name-component"> - <ci-icon - :status="status" /> + <ci-icon :status="status" /> <span class="ci-status-text"> - {{name}} + {{ name }} </span> </span> </template> diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue index 9b1bbb0906f..e027f08ff5c 100644 --- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue @@ -1,58 +1,58 @@ <script> -import jobComponent from './job_component.vue'; -import dropdownJobComponent from './dropdown_job_component.vue'; + import jobComponent from './job_component.vue'; + import dropdownJobComponent from './dropdown_job_component.vue'; -export default { - props: { - title: { - type: String, - required: true, + export default { + components: { + jobComponent, + dropdownJobComponent, }, - jobs: { - type: Array, - required: true, - }, - - isFirstColumn: { - type: Boolean, - required: false, - default: false, - }, + props: { + title: { + type: String, + required: true, + }, - stageConnectorClass: { - type: String, - required: false, - default: '', - }, - }, + jobs: { + type: Array, + required: true, + }, - components: { - jobComponent, - dropdownJobComponent, - }, + isFirstColumn: { + type: Boolean, + required: false, + default: false, + }, - methods: { - firstJob(list) { - return list[0]; + stageConnectorClass: { + type: String, + required: false, + default: '', + }, }, - jobId(job) { - return `ci-badge-${job.name}`; - }, + methods: { + firstJob(list) { + return list[0]; + }, + + jobId(job) { + return `ci-badge-${job.name}`; + }, - buildConnnectorClass(index) { - return index === 0 && !this.isFirstColumn ? 'left-connector' : ''; + buildConnnectorClass(index) { + return index === 0 && !this.isFirstColumn ? 'left-connector' : ''; + }, }, - }, -}; + }; </script> <template> <li class="stage-column" :class="stageConnectorClass"> <div class="stage-name"> - {{title}} + {{ title }} </div> <div class="builds-container"> <ul> @@ -61,7 +61,8 @@ export default { :key="job.id" class="build" :class="buildConnnectorClass(index)" - :id="jobId(job)"> + :id="jobId(job)" + > <div class="curve"></div> @@ -69,12 +70,12 @@ export default { v-if="job.size === 1" :job="job" css-class-job-name="build-content" - /> + /> <dropdown-job-component v-if="job.size > 1" :job="job" - /> + /> </li> </ul> diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue index 2a1ecac3707..942acc8c412 100644 --- a/app/assets/javascripts/pipelines/components/header_component.vue +++ b/app/assets/javascripts/pipelines/components/header_component.vue @@ -1,82 +1,81 @@ <script> -import ciHeader from '../../vue_shared/components/header_ci_component.vue'; -import eventHub from '../event_hub'; -import loadingIcon from '../../vue_shared/components/loading_icon.vue'; + import ciHeader from '../../vue_shared/components/header_ci_component.vue'; + import eventHub from '../event_hub'; + import loadingIcon from '../../vue_shared/components/loading_icon.vue'; -export default { - name: 'PipelineHeaderSection', - props: { - pipeline: { - type: Object, - required: true, + export default { + name: 'PipelineHeaderSection', + components: { + ciHeader, + loadingIcon, }, - isLoading: { - type: Boolean, - required: true, + props: { + pipeline: { + type: Object, + required: true, + }, + isLoading: { + type: Boolean, + required: true, + }, }, - }, - components: { - ciHeader, - loadingIcon, - }, - - data() { - return { - actions: this.getActions(), - }; - }, - - computed: { - status() { - return this.pipeline.details && this.pipeline.details.status; + data() { + return { + actions: this.getActions(), + }; }, - shouldRenderContent() { - return !this.isLoading && Object.keys(this.pipeline).length; + + computed: { + status() { + return this.pipeline.details && this.pipeline.details.status; + }, + shouldRenderContent() { + return !this.isLoading && Object.keys(this.pipeline).length; + }, }, - }, - methods: { - postAction(action) { - const index = this.actions.indexOf(action); + watch: { + pipeline() { + this.actions = this.getActions(); + }, + }, - this.$set(this.actions[index], 'isLoading', true); + methods: { + postAction(action) { + const index = this.actions.indexOf(action); - eventHub.$emit('headerPostAction', action); - }, + this.$set(this.actions[index], 'isLoading', true); - getActions() { - const actions = []; + eventHub.$emit('headerPostAction', action); + }, - if (this.pipeline.retry_path) { - actions.push({ - label: 'Retry', - path: this.pipeline.retry_path, - cssClass: 'js-retry-button btn btn-inverted-secondary', - type: 'button', - isLoading: false, - }); - } + getActions() { + const actions = []; - if (this.pipeline.cancel_path) { - actions.push({ - label: 'Cancel running', - path: this.pipeline.cancel_path, - cssClass: 'js-btn-cancel-pipeline btn btn-danger', - type: 'button', - isLoading: false, - }); - } + if (this.pipeline.retry_path) { + actions.push({ + label: 'Retry', + path: this.pipeline.retry_path, + cssClass: 'js-retry-button btn btn-inverted-secondary', + type: 'button', + isLoading: false, + }); + } - return actions; - }, - }, + if (this.pipeline.cancel_path) { + actions.push({ + label: 'Cancel running', + path: this.pipeline.cancel_path, + cssClass: 'js-btn-cancel-pipeline btn btn-danger', + type: 'button', + isLoading: false, + }); + } - watch: { - pipeline() { - this.actions = this.getActions(); + return actions; + }, }, - }, -}; + }; </script> <template> <div class="pipeline-header-container"> @@ -89,9 +88,10 @@ export default { :user="pipeline.user" :actions="actions" @actionClicked="postAction" - /> + /> <loading-icon v-if="isLoading" - size="2"/> + size="2" + /> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue index 1e2f45333a5..ceb4d9ca604 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue @@ -4,6 +4,13 @@ import popover from '../../vue_shared/directives/popover'; export default { + components: { + userAvatarLink, + }, + directives: { + tooltip, + popover, + }, props: { pipeline: { type: Object, @@ -14,13 +21,6 @@ required: true, }, }, - components: { - userAvatarLink, - }, - directives: { - tooltip, - popover, - }, computed: { user() { return this.pipeline.user; @@ -50,7 +50,7 @@ <a :href="pipeline.path" class="js-pipeline-url-link"> - <span class="pipeline-id">#{{pipeline.id}}</span> + <span class="pipeline-id">#{{ pipeline.id }}</span> </a> <span>by</span> <user-avatar-link diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue index fe1f3b4246a..698d7dfe736 100644 --- a/app/assets/javascripts/pipelines/components/pipelines.vue +++ b/app/assets/javascripts/pipelines/components/pipelines.vue @@ -13,6 +13,15 @@ import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin'; export default { + components: { + tablePagination, + navigationTabs, + navigationControls, + }, + mixins: [ + pipelinesMixin, + CIPaginationMixin, + ], props: { store: { type: Object, @@ -28,15 +37,6 @@ default: 'root', }, }, - components: { - tablePagination, - navigationTabs, - navigationControls, - }, - mixins: [ - pipelinesMixin, - CIPaginationMixin, - ], data() { const pipelinesData = document.querySelector('#pipelines-list-vue').dataset; @@ -214,7 +214,7 @@ :tabs="tabs" @onChangeTab="onChangeTab" scope="pipelines" - /> + /> <navigation-controls :new-pipeline-path="newPipelinePath" @@ -222,7 +222,7 @@ :help-page-path="helpPagePath" :ci-lint-path="ciLintPath" :can-create-pipeline="canCreatePipelineParsed " - /> + /> </div> <div class="content-list pipelines"> @@ -232,22 +232,23 @@ size="3" v-if="isLoading" class="prepend-top-20" - /> + /> <empty-state v-if="shouldRenderEmptyState" :help-page-path="helpPagePath" :empty-state-svg-path="emptyStateSvgPath" - /> + /> <error-state v-if="shouldRenderErrorState" :error-state-svg-path="errorStateSvgPath" - /> + /> <div class="blank-state-row" - v-if="shouldRenderNoPipelinesMessage"> + v-if="shouldRenderNoPipelinesMessage" + > <div class="blank-state-center"> <h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2> </div> @@ -255,21 +256,22 @@ <div class="table-holder" - v-if="shouldRenderTable"> + v-if="shouldRenderTable" + > <pipelines-table-component :pipelines="state.pipelines" :update-graph-dropdown="updateGraphDropdown" :auto-devops-help-path="autoDevopsPath" :view-type="viewType" - /> + /> </div> <table-pagination v-if="shouldRenderPagination" :change="onChangePage" :page-info="state.pageInfo" - /> + /> </div> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue index f3c0aca17ba..efda36c12d6 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue @@ -5,18 +5,18 @@ import tooltip from '../../vue_shared/directives/tooltip'; export default { - props: { - actions: { - type: Array, - required: true, - }, - }, directives: { tooltip, }, components: { loadingIcon, }, + props: { + actions: { + type: Array, + required: true, + }, + }, data() { return { playIconSvg, @@ -50,7 +50,8 @@ data-toggle="dropdown" data-placement="top" aria-label="Manual job" - :disabled="isLoading"> + :disabled="isLoading" + > <span v-html="playIconSvg"></span> <i class="fa fa-caret-down" @@ -60,14 +61,18 @@ </button> <ul class="dropdown-menu dropdown-menu-align-right"> - <li v-for="action in actions"> + <li + v-for="(action, i) in actions" + :key="i" + > <button type="button" class="js-pipeline-action-link no-btn btn" @click="onClickAction(action.path)" :class="{ disabled: isActionDisabled(action) }" - :disabled="isActionDisabled(action)"> - {{action.name}} + :disabled="isActionDisabled(action)" + > + {{ action.name }} </button> </li> </ul> diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue index 831aa92ac4f..1b9e0f917a4 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue @@ -3,46 +3,50 @@ import icon from '../../vue_shared/components/icon.vue'; export default { - props: { - artifacts: { - type: Array, - required: true, - }, - }, directives: { tooltip, }, components: { icon, }, + props: { + artifacts: { + type: Array, + required: true, + }, + }, }; </script> <template> <div class="btn-group" - role="group"> + role="group" + > <button v-tooltip class="dropdown-toggle btn btn-default build-artifacts js-pipeline-dropdown-download" title="Artifacts" data-placement="top" data-toggle="dropdown" - aria-label="Artifacts"> - <icon - name="download"> - </icon> + aria-label="Artifacts" + > + <icon name="download" /> <i class="fa fa-caret-down" - aria-hidden="true"> + aria-hidden="true" + > </i> </button> <ul class="dropdown-menu dropdown-menu-align-right"> - <li v-for="artifact in artifacts"> + <li + v-for="(artifact, i) in artifacts" + :key="i"> <a rel="nofollow" download - :href="artifact.path"> - Download {{artifact.name}} artifacts + :href="artifact.path" + > + Download {{ artifact.name }} artifacts </a> </li> </ul> diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue index 16a705cbaff..c6638cdcf1e 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue @@ -7,6 +7,9 @@ * Given an array of objects, renders a table. */ export default { + components: { + pipelinesTableRowComponent, + }, props: { pipelines: { type: Array, @@ -26,34 +29,36 @@ required: true, }, }, - components: { - pipelinesTableRowComponent, - }, }; </script> <template> <div class="ci-table"> <div class="gl-responsive-table-row table-row-header" - role="row"> + role="row" + > <div class="table-section section-10 js-pipeline-status pipeline-status" - role="rowheader"> + role="rowheader" + > Status </div> <div class="table-section section-15 js-pipeline-info pipeline-info" - role="rowheader"> + role="rowheader" + > Pipeline </div> <div class="table-section section-25 js-pipeline-commit pipeline-commit" - role="rowheader"> + role="rowheader" + > Commit </div> <div class="table-section section-15 js-pipeline-stages pipeline-stages" - role="rowheader"> + role="rowheader" + > Stages </div> </div> diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue index 33fbce993b2..670b777199c 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue @@ -1,227 +1,228 @@ <script> -/* eslint-disable no-param-reassign */ -import asyncButtonComponent from './async_button.vue'; -import pipelinesActionsComponent from './pipelines_actions.vue'; -import pipelinesArtifactsComponent from './pipelines_artifacts.vue'; -import ciBadge from '../../vue_shared/components/ci_badge_link.vue'; -import pipelineStage from './stage.vue'; -import pipelineUrl from './pipeline_url.vue'; -import pipelinesTimeago from './time_ago.vue'; -import commitComponent from '../../vue_shared/components/commit.vue'; + /* eslint-disable no-param-reassign */ + import asyncButtonComponent from './async_button.vue'; + import pipelinesActionsComponent from './pipelines_actions.vue'; + import pipelinesArtifactsComponent from './pipelines_artifacts.vue'; + import ciBadge from '../../vue_shared/components/ci_badge_link.vue'; + import pipelineStage from './stage.vue'; + import pipelineUrl from './pipeline_url.vue'; + import pipelinesTimeago from './time_ago.vue'; + import commitComponent from '../../vue_shared/components/commit.vue'; -/** - * Pipeline table row. - * - * Given the received object renders a table row in the pipelines' table. - */ -export default { - props: { - pipeline: { - type: Object, - required: true, + /** + * Pipeline table row. + * + * Given the received object renders a table row in the pipelines' table. + */ + export default { + components: { + asyncButtonComponent, + pipelinesActionsComponent, + pipelinesArtifactsComponent, + commitComponent, + pipelineStage, + pipelineUrl, + ciBadge, + pipelinesTimeago, }, - updateGraphDropdown: { - type: Boolean, - required: false, - default: false, + props: { + pipeline: { + type: Object, + required: true, + }, + updateGraphDropdown: { + type: Boolean, + required: false, + default: false, + }, + autoDevopsHelpPath: { + type: String, + required: true, + }, + viewType: { + type: String, + required: true, + }, }, - autoDevopsHelpPath: { - type: String, - required: true, - }, - viewType: { - type: String, - required: true, - }, - }, - components: { - asyncButtonComponent, - pipelinesActionsComponent, - pipelinesArtifactsComponent, - commitComponent, - pipelineStage, - pipelineUrl, - ciBadge, - pipelinesTimeago, - }, - computed: { - /** - * If provided, returns the commit tag. - * Needed to render the commit component column. - * - * This field needs a lot of verification, because of different possible cases: - * - * 1. person who is an author of a commit might be a GitLab user - * 2. if person who is an author of a commit is a GitLab user he/she can have a GitLab avatar - * 3. If GitLab user does not have avatar he/she might have a Gravatar - * 4. If committer is not a GitLab User he/she can have a Gravatar - * 5. We do not have consistent API object in this case - * 6. We should improve API and the code - * - * @returns {Object|Undefined} - */ - commitAuthor() { - let commitAuthorInformation; + computed: { + /** + * If provided, returns the commit tag. + * Needed to render the commit component column. + * + * This field needs a lot of verification, because of different possible cases: + * + * 1. person who is an author of a commit might be a GitLab user + * 2. if person who is an author of a commit is a GitLab user he/she can have a GitLab avatar + * 3. If GitLab user does not have avatar he/she might have a Gravatar + * 4. If committer is not a GitLab User he/she can have a Gravatar + * 5. We do not have consistent API object in this case + * 6. We should improve API and the code + * + * @returns {Object|Undefined} + */ + commitAuthor() { + let commitAuthorInformation; - if (!this.pipeline || !this.pipeline.commit) { - return null; - } + if (!this.pipeline || !this.pipeline.commit) { + return null; + } - // 1. person who is an author of a commit might be a GitLab user - if (this.pipeline.commit.author) { - // 2. if person who is an author of a commit is a GitLab user - // he/she can have a GitLab avatar - if (this.pipeline.commit.author.avatar_url) { - commitAuthorInformation = this.pipeline.commit.author; + // 1. person who is an author of a commit might be a GitLab user + if (this.pipeline.commit.author) { + // 2. if person who is an author of a commit is a GitLab user + // he/she can have a GitLab avatar + if (this.pipeline.commit.author.avatar_url) { + commitAuthorInformation = this.pipeline.commit.author; - // 3. If GitLab user does not have avatar he/she might have a Gravatar - } else if (this.pipeline.commit.author_gravatar_url) { - commitAuthorInformation = Object.assign({}, this.pipeline.commit.author, { + // 3. If GitLab user does not have avatar he/she might have a Gravatar + } else if (this.pipeline.commit.author_gravatar_url) { + commitAuthorInformation = Object.assign({}, this.pipeline.commit.author, { + avatar_url: this.pipeline.commit.author_gravatar_url, + }); + } + // 4. If committer is not a GitLab User he/she can have a Gravatar + } else { + commitAuthorInformation = { avatar_url: this.pipeline.commit.author_gravatar_url, - }); + path: `mailto:${this.pipeline.commit.author_email}`, + username: this.pipeline.commit.author_name, + }; } - // 4. If committer is not a GitLab User he/she can have a Gravatar - } else { - commitAuthorInformation = { - avatar_url: this.pipeline.commit.author_gravatar_url, - path: `mailto:${this.pipeline.commit.author_email}`, - username: this.pipeline.commit.author_name, - }; - } - return commitAuthorInformation; - }, + return commitAuthorInformation; + }, - /** - * If provided, returns the commit tag. - * Needed to render the commit component column. - * - * @returns {String|Undefined} - */ - commitTag() { - if (this.pipeline.ref && - this.pipeline.ref.tag) { - return this.pipeline.ref.tag; - } - return undefined; - }, + /** + * If provided, returns the commit tag. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitTag() { + if (this.pipeline.ref && + this.pipeline.ref.tag) { + return this.pipeline.ref.tag; + } + return undefined; + }, - /** - * If provided, returns the commit ref. - * Needed to render the commit component column. - * - * Matches `path` prop sent in the API to `ref_url` prop needed - * in the commit component. - * - * @returns {Object|Undefined} - */ - commitRef() { - if (this.pipeline.ref) { - return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => { - if (prop === 'path') { - accumulator.ref_url = this.pipeline.ref[prop]; - } else { - accumulator[prop] = this.pipeline.ref[prop]; - } - return accumulator; - }, {}); - } + /** + * If provided, returns the commit ref. + * Needed to render the commit component column. + * + * Matches `path` prop sent in the API to `ref_url` prop needed + * in the commit component. + * + * @returns {Object|Undefined} + */ + commitRef() { + if (this.pipeline.ref) { + return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => { + if (prop === 'path') { + accumulator.ref_url = this.pipeline.ref[prop]; + } else { + accumulator[prop] = this.pipeline.ref[prop]; + } + return accumulator; + }, {}); + } - return undefined; - }, + return undefined; + }, - /** - * If provided, returns the commit url. - * Needed to render the commit component column. - * - * @returns {String|Undefined} - */ - commitUrl() { - if (this.pipeline.commit && - this.pipeline.commit.commit_path) { - return this.pipeline.commit.commit_path; - } - return undefined; - }, + /** + * If provided, returns the commit url. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitUrl() { + if (this.pipeline.commit && + this.pipeline.commit.commit_path) { + return this.pipeline.commit.commit_path; + } + return undefined; + }, - /** - * If provided, returns the commit short sha. - * Needed to render the commit component column. - * - * @returns {String|Undefined} - */ - commitShortSha() { - if (this.pipeline.commit && - this.pipeline.commit.short_id) { - return this.pipeline.commit.short_id; - } - return undefined; - }, + /** + * If provided, returns the commit short sha. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitShortSha() { + if (this.pipeline.commit && + this.pipeline.commit.short_id) { + return this.pipeline.commit.short_id; + } + return undefined; + }, - /** - * If provided, returns the commit title. - * Needed to render the commit component column. - * - * @returns {String|Undefined} - */ - commitTitle() { - if (this.pipeline.commit && - this.pipeline.commit.title) { - return this.pipeline.commit.title; - } - return undefined; - }, + /** + * If provided, returns the commit title. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitTitle() { + if (this.pipeline.commit && + this.pipeline.commit.title) { + return this.pipeline.commit.title; + } + return undefined; + }, - /** - * Timeago components expects a number - * - * @return {type} description - */ - pipelineDuration() { - if (this.pipeline.details && this.pipeline.details.duration) { - return this.pipeline.details.duration; - } + /** + * Timeago components expects a number + * + * @return {type} description + */ + pipelineDuration() { + if (this.pipeline.details && this.pipeline.details.duration) { + return this.pipeline.details.duration; + } - return 0; - }, + return 0; + }, - /** - * Timeago component expects a String. - * - * @return {String} - */ - pipelineFinishedAt() { - if (this.pipeline.details && this.pipeline.details.finished_at) { - return this.pipeline.details.finished_at; - } + /** + * Timeago component expects a String. + * + * @return {String} + */ + pipelineFinishedAt() { + if (this.pipeline.details && this.pipeline.details.finished_at) { + return this.pipeline.details.finished_at; + } - return ''; - }, + return ''; + }, - pipelineStatus() { - if (this.pipeline.details && this.pipeline.details.status) { - return this.pipeline.details.status; - } - return {}; - }, + pipelineStatus() { + if (this.pipeline.details && this.pipeline.details.status) { + return this.pipeline.details.status; + } + return {}; + }, - displayPipelineActions() { - return this.pipeline.flags.retryable || - this.pipeline.flags.cancelable || - this.pipeline.details.manual_actions.length || - this.pipeline.details.artifacts.length; - }, + displayPipelineActions() { + return this.pipeline.flags.retryable || + this.pipeline.flags.cancelable || + this.pipeline.details.manual_actions.length || + this.pipeline.details.artifacts.length; + }, - isChildView() { - return this.viewType === 'child'; + isChildView() { + return this.viewType === 'child'; + }, }, - }, -}; + }; </script> <template> <div class="commit gl-responsive-table-row"> <div class="table-section section-10 commit-link"> - <div class="table-mobile-header" + <div + class="table-mobile-header" role="rowheader"> Status </div> @@ -229,14 +230,14 @@ export default { <ci-badge :status="pipelineStatus" :show-text="!isChildView" - /> + /> </div> </div> <pipeline-url :pipeline="pipeline" :auto-devops-help-path="autoDevopsHelpPath" - /> + /> <div class="table-section section-25"> <div @@ -253,7 +254,7 @@ export default { :title="commitTitle" :author="commitAuthor" :show-branch="!isChildView" - /> + /> </div> </div> @@ -264,21 +265,24 @@ export default { Stages </div> <div class="table-mobile-content"> - <div class="stage-container dropdown js-mini-pipeline-graph" - v-if="pipeline.details.stages.length > 0" - v-for="stage in pipeline.details.stages"> - <pipeline-stage - :stage="stage" - :update-dropdown="updateGraphDropdown" + <template v-if="pipeline.details.stages.length > 0"> + <div + class="stage-container dropdown js-mini-pipeline-graph" + v-for="(stage, index) in pipeline.details.stages" + :key="index"> + <pipeline-stage + :stage="stage" + :update-dropdown="updateGraphDropdown" /> - </div> + </div> + </template> </div> </div> <pipelines-timeago :duration="pipelineDuration" :finished-time="pipelineFinishedAt" - /> + /> <div v-if="displayPipelineActions" @@ -287,13 +291,13 @@ export default { <pipelines-actions-component v-if="pipeline.details.manual_actions.length" :actions="pipeline.details.manual_actions" - /> + /> <pipelines-artifacts-component v-if="pipeline.details.artifacts.length" class="hidden-xs hidden-sm" :artifacts="pipeline.details.artifacts" - /> + /> <async-button-component v-if="pipeline.flags.retryable" @@ -301,7 +305,7 @@ export default { css-class="js-pipelines-retry-button btn-default btn-retry" title="Retry" icon="repeat" - /> + /> <async-button-component v-if="pipeline.flags.cancelable" @@ -310,7 +314,7 @@ export default { title="Cancel" icon="remove" confirm-action-message="Are you sure you want to cancel this pipeline?" - /> + /> </div> </div> </div> diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index 021f271b267..58806aa114a 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -1,135 +1,135 @@ <script> -/** - * Renders each stage of the pipeline mini graph. - * - * Given the provided endpoint will make a request to - * fetch the dropdown data when the stage is clicked. - * - * Request is made inside this component to make it reusable between: - * 1. Pipelines main table - * 2. Pipelines table in commit and Merge request views - * 3. Merge request widget - * 4. Commit widget - */ - -import Flash from '../../flash'; -import icon from '../../vue_shared/components/icon.vue'; -import loadingIcon from '../../vue_shared/components/loading_icon.vue'; -import tooltip from '../../vue_shared/directives/tooltip'; - -export default { - props: { - stage: { - type: Object, - required: true, + /** + * Renders each stage of the pipeline mini graph. + * + * Given the provided endpoint will make a request to + * fetch the dropdown data when the stage is clicked. + * + * Request is made inside this component to make it reusable between: + * 1. Pipelines main table + * 2. Pipelines table in commit and Merge request views + * 3. Merge request widget + * 4. Commit widget + */ + + import Flash from '../../flash'; + import icon from '../../vue_shared/components/icon.vue'; + import loadingIcon from '../../vue_shared/components/loading_icon.vue'; + import tooltip from '../../vue_shared/directives/tooltip'; + + export default { + components: { + loadingIcon, + icon, }, - updateDropdown: { - type: Boolean, - required: false, - default: false, + directives: { + tooltip, }, - }, - - directives: { - tooltip, - }, - - data() { - return { - isLoading: false, - dropdownContent: '', - }; - }, - - components: { - loadingIcon, - icon, - }, - - updated() { - if (this.dropdownContent.length > 0) { - this.stopDropdownClickPropagation(); - } - }, - - watch: { - updateDropdown() { - if (this.updateDropdown && - this.isDropdownOpen() && - !this.isLoading) { - this.fetchJobs(); - } - }, - }, - methods: { - onClickStage() { - if (!this.isDropdownOpen()) { - this.isLoading = true; - this.fetchJobs(); - } + props: { + stage: { + type: Object, + required: true, + }, + + updateDropdown: { + type: Boolean, + required: false, + default: false, + }, }, - fetchJobs() { - this.$http.get(this.stage.dropdown_path) - .then(response => response.json()) - .then((data) => { - this.dropdownContent = data.html; - this.isLoading = false; - }) - .catch(() => { - this.closeDropdown(); - this.isLoading = false; - - const flash = new Flash('Something went wrong on our end.'); - return flash; - }); + data() { + return { + isLoading: false, + dropdownContent: '', + }; }, - /** - * When the user right clicks or cmd/ctrl + click in the job name - * the dropdown should not be closed and the link should open in another tab, - * so we stop propagation of the click event inside the dropdown. - * - * Since this component is rendered multiple times per page we need to guarantee we only - * target the click event of this component. - */ - stopDropdownClickPropagation() { - $(this.$el.querySelectorAll('.js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item')) - .on('click', (e) => { - e.stopPropagation(); - }); - }, + computed: { + dropdownClass() { + return this.dropdownContent.length > 0 ? + 'js-builds-dropdown-container' : + 'js-builds-dropdown-loading'; + }, - closeDropdown() { - if (this.isDropdownOpen()) { - $(this.$refs.dropdown).dropdown('toggle'); - } - }, + triggerButtonClass() { + return `ci-status-icon-${this.stage.status.group}`; + }, - isDropdownOpen() { - return this.$el.classList.contains('open'); + borderlessIcon() { + return `${this.stage.status.icon}_borderless`; + }, }, - }, - computed: { - dropdownClass() { - return this.dropdownContent.length > 0 ? - 'js-builds-dropdown-container' : - 'js-builds-dropdown-loading'; + watch: { + updateDropdown() { + if (this.updateDropdown && + this.isDropdownOpen() && + !this.isLoading) { + this.fetchJobs(); + } + }, }, - triggerButtonClass() { - return `ci-status-icon-${this.stage.status.group}`; + updated() { + if (this.dropdownContent.length > 0) { + this.stopDropdownClickPropagation(); + } }, - borderlessIcon() { - return `${this.stage.status.icon}_borderless`; + methods: { + onClickStage() { + if (!this.isDropdownOpen()) { + this.isLoading = true; + this.fetchJobs(); + } + }, + + fetchJobs() { + this.$http.get(this.stage.dropdown_path) + .then(response => response.json()) + .then((data) => { + this.dropdownContent = data.html; + this.isLoading = false; + }) + .catch(() => { + this.closeDropdown(); + this.isLoading = false; + + const flash = new Flash('Something went wrong on our end.'); + return flash; + }); + }, + + /** + * When the user right clicks or cmd/ctrl + click in the job name + * the dropdown should not be closed and the link should open in another tab, + * so we stop propagation of the click event inside the dropdown. + * + * Since this component is rendered multiple times per page we need to guarantee we only + * target the click event of this component. + */ + stopDropdownClickPropagation() { + $(this.$el.querySelectorAll('.js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item')) + .on('click', (e) => { + e.stopPropagation(); + }); + }, + + closeDropdown() { + if (this.isDropdownOpen()) { + $(this.$refs.dropdown).dropdown('toggle'); + } + }, + + isDropdownOpen() { + return this.$el.classList.contains('open'); + }, }, - }, -}; + }; </script> <template> @@ -145,36 +145,41 @@ export default { type="button" id="stageDropdown" aria-haspopup="true" - aria-expanded="false"> + aria-expanded="false" + > <span aria-hidden="true" - :aria-label="stage.title"> - <icon - :name="borderlessIcon"/> + :aria-label="stage.title" + > + <icon :name="borderlessIcon" /> </span> <i class="fa fa-caret-down" - aria-hidden="true"> + aria-hidden="true" + > </i> </button> <ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container" - aria-labelledby="stageDropdown"> + aria-labelledby="stageDropdown" + > <li :class="dropdownClass" - class="js-builds-dropdown-list scrollable-menu"> + class="js-builds-dropdown-list scrollable-menu" + > <loading-icon v-if="isLoading"/> <ul v-else - v-html="dropdownContent"> + v-html="dropdownContent" + > </ul> </li> </ul> </div> -</script> +</template> diff --git a/app/assets/javascripts/pipelines/components/time_ago.vue b/app/assets/javascripts/pipelines/components/time_ago.vue index 037684b4e72..cd54d26c9d3 100644 --- a/app/assets/javascripts/pipelines/components/time_ago.vue +++ b/app/assets/javascripts/pipelines/components/time_ago.vue @@ -5,6 +5,12 @@ import timeagoMixin from '../../vue_shared/mixins/timeago'; export default { + directives: { + tooltip, + }, + mixins: [ + timeagoMixin, + ], props: { finishedTime: { type: String, @@ -15,12 +21,6 @@ required: true, }, }, - mixins: [ - timeagoMixin, - ], - directives: { - tooltip, - }, data() { return { iconTimerSvg, @@ -60,26 +60,29 @@ <div class="table-section section-15 pipelines-time-ago"> <div class="table-mobile-header" - role="rowheader"> + role="rowheader" + > Duration </div> <div class="table-mobile-content"> <p class="duration" - v-if="hasDuration"> - <span - v-html="iconTimerSvg"> + v-if="hasDuration" + > + <span v-html="iconTimerSvg"> </span> - {{durationFormated}} + {{ durationFormated }} </p> <p class="finished-at hidden-xs hidden-sm" - v-if="hasFinishedTime"> + v-if="hasFinishedTime" + > <i class="fa fa-calendar" - aria-hidden="true"> + aria-hidden="true" + > </i> <time @@ -87,9 +90,9 @@ data-placement="top" data-container="body" :title="tooltipTitle(finishedTime)"> - {{timeFormated(finishedTime)}} + {{ timeFormated(finishedTime) }} </time> </p> </div> </div> -</script> +</template> diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue index 67ed7cc0cdf..03037567f43 100644 --- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue +++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue @@ -4,6 +4,9 @@ import csrf from '../../../lib/utils/csrf'; export default { + components: { + modal, + }, props: { actionUrl: { type: String, @@ -25,9 +28,6 @@ isOpen: false, }; }, - components: { - modal, - }, computed: { csrfToken() { return csrf.token; @@ -99,7 +99,9 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), @toggle="toggleOpen" @submit="onSubmit"> - <template slot="body" slot-scope="props"> + <template + slot="body" + slot-scope="props"> <p v-html="props.text"></p> <form @@ -110,13 +112,19 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), <input type="hidden" name="_method" - value="delete" /> + value="delete" + /> <input type="hidden" name="authenticity_token" - :value="csrfToken" /> + :value="csrfToken" + /> - <p id="input-label" v-html="inputLabel"></p> + <p + id="input-label" + v-html="inputLabel" + > + </p> <input v-if="confirmWithPassword" @@ -124,14 +132,16 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), class="form-control" type="password" v-model="enteredPassword" - aria-labelledby="input-label" /> + aria-labelledby="input-label" + /> <input v-else name="username" class="form-control" type="text" v-model="enteredUsername" - aria-labelledby="input-label" /> + aria-labelledby="input-label" + /> </form> </template> @@ -140,7 +150,8 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), <button type="button" class="btn btn-danger" - @click="toggleOpen(true)"> + @click="toggleOpen(true)" + > {{ s__('Profiles|Delete account') }} </button> </div> diff --git a/app/assets/javascripts/projects/permissions/components/project_feature_setting.vue b/app/assets/javascripts/projects/permissions/components/project_feature_setting.vue index 8fce4c63872..3ebfe82597a 100644 --- a/app/assets/javascripts/projects/permissions/components/project_feature_setting.vue +++ b/app/assets/javascripts/projects/permissions/components/project_feature_setting.vue @@ -1,77 +1,80 @@ <script> -import projectFeatureToggle from '../../../vue_shared/components/toggle_button.vue'; + import projectFeatureToggle from '../../../vue_shared/components/toggle_button.vue'; -export default { - props: { - name: { - type: String, - required: false, - default: '', + export default { + components: { + projectFeatureToggle, }, - options: { - type: Array, - required: false, - default: () => [], - }, - value: { - type: Number, - required: false, - default: 0, - }, - disabledInput: { - type: Boolean, - required: false, - default: false, - }, - }, - - components: { - projectFeatureToggle, - }, - computed: { - featureEnabled() { - return this.value !== 0; + model: { + prop: 'value', + event: 'change', }, - displayOptions() { - if (this.featureEnabled) { - return this.options; - } - return [ - [0, 'Enable feature to choose access level'], - ]; + props: { + name: { + type: String, + required: false, + default: '', + }, + options: { + type: Array, + required: false, + default: () => [], + }, + value: { + type: Number, + required: false, + default: 0, + }, + disabledInput: { + type: Boolean, + required: false, + default: false, + }, }, - displaySelectInput() { - return this.disabledInput || !this.featureEnabled || this.displayOptions.length < 2; - }, - }, + computed: { + featureEnabled() { + return this.value !== 0; + }, - model: { - prop: 'value', - event: 'change', - }, + displayOptions() { + if (this.featureEnabled) { + return this.options; + } + return [ + [0, 'Enable feature to choose access level'], + ]; + }, - methods: { - toggleFeature(featureEnabled) { - if (featureEnabled === false || this.options.length < 1) { - this.$emit('change', 0); - } else { - const [firstOptionValue] = this.options[this.options.length - 1]; - this.$emit('change', firstOptionValue); - } + displaySelectInput() { + return this.disabledInput || !this.featureEnabled || this.displayOptions.length < 2; + }, }, - selectOption(e) { - this.$emit('change', Number(e.target.value)); + methods: { + toggleFeature(featureEnabled) { + if (featureEnabled === false || this.options.length < 1) { + this.$emit('change', 0); + } else { + const [firstOptionValue] = this.options[this.options.length - 1]; + this.$emit('change', firstOptionValue); + } + }, + + selectOption(e) { + this.$emit('change', Number(e.target.value)); + }, }, - }, -}; + }; </script> <template> - <div class="project-feature-controls" :data-for="name"> + <div + class="project-feature-controls" + :data-for="name" + > <input v-if="name" type="hidden" @@ -81,7 +84,7 @@ export default { <project-feature-toggle :value="featureEnabled" @change="toggleFeature" - :disabledInput="disabledInput" + :disabled-input="disabledInput" /> <div class="select-wrapper"> <select @@ -95,10 +98,14 @@ export default { :value="optionValue" :selected="optionValue === value" > - {{optionName}} + {{ optionName }} </option> </select> - <i aria-hidden="true" class="fa fa-chevron-down"></i> + <i + aria-hidden="true" + class="fa fa-chevron-down" + > + </i> </div> </div> </template> diff --git a/app/assets/javascripts/projects/permissions/components/project_setting_row.vue b/app/assets/javascripts/projects/permissions/components/project_setting_row.vue index 6140d74fea8..138c8b2ed68 100644 --- a/app/assets/javascripts/projects/permissions/components/project_setting_row.vue +++ b/app/assets/javascripts/projects/permissions/components/project_setting_row.vue @@ -1,35 +1,50 @@ <script> -export default { - props: { - label: { - type: String, - required: false, - default: null, + export default { + props: { + label: { + type: String, + required: false, + default: null, + }, + helpPath: { + type: String, + required: false, + default: null, + }, + helpText: { + type: String, + required: false, + default: null, + }, }, - helpPath: { - type: String, - required: false, - default: null, - }, - helpText: { - type: String, - required: false, - default: null, - }, - }, -}; + }; </script> <template> <div class="project-feature-row"> - <label v-if="label" class="label-light"> - {{label}} - <a v-if="helpPath" :href="helpPath" target="_blank"> - <i aria-hidden="true" data-hidden="true" class="fa fa-question-circle"></i> + <label + v-if="label" + class="label-light" + > + {{ label }} + <a + v-if="helpPath" + :href="helpPath" + target="_blank" + > + <i + aria-hidden="true" + data-hidden="true" + class="fa fa-question-circle" + > + </i> </a> </label> - <span v-if="helpText" class="help-block"> - {{helpText}} + <span + v-if="helpText" + class="help-block" + > + {{ helpText }} </span> <slot /> </div> diff --git a/app/assets/javascripts/projects/permissions/components/settings_panel.vue b/app/assets/javascripts/projects/permissions/components/settings_panel.vue index 639429baf26..dc2475cead6 100644 --- a/app/assets/javascripts/projects/permissions/components/settings_panel.vue +++ b/app/assets/javascripts/projects/permissions/components/settings_panel.vue @@ -1,172 +1,171 @@ <script> -import projectFeatureSetting from './project_feature_setting.vue'; -import projectFeatureToggle from '../../../vue_shared/components/toggle_button.vue'; -import projectSettingRow from './project_setting_row.vue'; -import { visibilityOptions, visibilityLevelDescriptions } from '../constants'; -import { toggleHiddenClassBySelector } from '../external'; + import projectFeatureSetting from './project_feature_setting.vue'; + import projectFeatureToggle from '../../../vue_shared/components/toggle_button.vue'; + import projectSettingRow from './project_setting_row.vue'; + import { visibilityOptions, visibilityLevelDescriptions } from '../constants'; + import { toggleHiddenClassBySelector } from '../external'; -export default { - props: { - currentSettings: { - type: Object, - required: true, + export default { + components: { + projectFeatureSetting, + projectFeatureToggle, + projectSettingRow, }, - canChangeVisibilityLevel: { - type: Boolean, - required: false, - default: false, - }, - allowedVisibilityOptions: { - type: Array, - required: false, - default: () => [0, 10, 20], - }, - lfsAvailable: { - type: Boolean, - required: false, - default: false, - }, - registryAvailable: { - type: Boolean, - required: false, - default: false, - }, - visibilityHelpPath: { - type: String, - required: false, - }, - lfsHelpPath: { - type: String, - required: false, - }, - registryHelpPath: { - type: String, - required: false, - }, - }, - data() { - const defaults = { - visibilityOptions, - visibilityLevel: visibilityOptions.PUBLIC, - issuesAccessLevel: 20, - repositoryAccessLevel: 20, - mergeRequestsAccessLevel: 20, - buildsAccessLevel: 20, - wikiAccessLevel: 20, - snippetsAccessLevel: 20, - containerRegistryEnabled: true, - lfsEnabled: true, - requestAccessEnabled: true, - highlightChangesClass: false, - }; - - return { ...defaults, ...this.currentSettings }; - }, - - components: { - projectFeatureSetting, - projectFeatureToggle, - projectSettingRow, - }, - - computed: { - featureAccessLevelOptions() { - const options = [ - [10, 'Only Project Members'], - ]; - if (this.visibilityLevel !== visibilityOptions.PRIVATE) { - options.push([20, 'Everyone With Access']); - } - return options; + props: { + currentSettings: { + type: Object, + required: true, + }, + canChangeVisibilityLevel: { + type: Boolean, + required: false, + default: false, + }, + allowedVisibilityOptions: { + type: Array, + required: false, + default: () => [0, 10, 20], + }, + lfsAvailable: { + type: Boolean, + required: false, + default: false, + }, + registryAvailable: { + type: Boolean, + required: false, + default: false, + }, + visibilityHelpPath: { + type: String, + required: false, + }, + lfsHelpPath: { + type: String, + required: false, + }, + registryHelpPath: { + type: String, + required: false, + }, }, - repoFeatureAccessLevelOptions() { - return this.featureAccessLevelOptions.filter( - ([value]) => value <= this.repositoryAccessLevel, - ); - }, + data() { + const defaults = { + visibilityOptions, + visibilityLevel: visibilityOptions.PUBLIC, + issuesAccessLevel: 20, + repositoryAccessLevel: 20, + mergeRequestsAccessLevel: 20, + buildsAccessLevel: 20, + wikiAccessLevel: 20, + snippetsAccessLevel: 20, + containerRegistryEnabled: true, + lfsEnabled: true, + requestAccessEnabled: true, + highlightChangesClass: false, + }; - repositoryEnabled() { - return this.repositoryAccessLevel > 0; + return { ...defaults, ...this.currentSettings }; }, - visibilityLevelDescription() { - return visibilityLevelDescriptions[this.visibilityLevel]; - }, - }, + computed: { + featureAccessLevelOptions() { + const options = [ + [10, 'Only Project Members'], + ]; + if (this.visibilityLevel !== visibilityOptions.PRIVATE) { + options.push([20, 'Everyone With Access']); + } + return options; + }, - methods: { - highlightChanges() { - this.highlightChangesClass = true; - this.$nextTick(() => { - this.highlightChangesClass = false; - }); - }, + repoFeatureAccessLevelOptions() { + return this.featureAccessLevelOptions.filter( + ([value]) => value <= this.repositoryAccessLevel, + ); + }, - visibilityAllowed(option) { - return this.allowedVisibilityOptions.includes(option); - }, - }, + repositoryEnabled() { + return this.repositoryAccessLevel > 0; + }, - watch: { - visibilityLevel(value, oldValue) { - if (value === visibilityOptions.PRIVATE) { - // when private, features are restricted to "only team members" - this.issuesAccessLevel = Math.min(10, this.issuesAccessLevel); - this.repositoryAccessLevel = Math.min(10, this.repositoryAccessLevel); - this.mergeRequestsAccessLevel = Math.min(10, this.mergeRequestsAccessLevel); - this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel); - this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel); - this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel); - this.highlightChanges(); - } else if (oldValue === visibilityOptions.PRIVATE) { - // if changing away from private, make enabled features more permissive - if (this.issuesAccessLevel > 0) this.issuesAccessLevel = 20; - if (this.repositoryAccessLevel > 0) this.repositoryAccessLevel = 20; - if (this.mergeRequestsAccessLevel > 0) this.mergeRequestsAccessLevel = 20; - if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20; - if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20; - if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20; - this.highlightChanges(); - } + visibilityLevelDescription() { + return visibilityLevelDescriptions[this.visibilityLevel]; + }, }, - repositoryAccessLevel(value, oldValue) { - if (value < oldValue) { - // sub-features cannot have more premissive access level - this.mergeRequestsAccessLevel = Math.min(this.mergeRequestsAccessLevel, value); - this.buildsAccessLevel = Math.min(this.buildsAccessLevel, value); + watch: { + visibilityLevel(value, oldValue) { + if (value === visibilityOptions.PRIVATE) { + // when private, features are restricted to "only team members" + this.issuesAccessLevel = Math.min(10, this.issuesAccessLevel); + this.repositoryAccessLevel = Math.min(10, this.repositoryAccessLevel); + this.mergeRequestsAccessLevel = Math.min(10, this.mergeRequestsAccessLevel); + this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel); + this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel); + this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel); + this.highlightChanges(); + } else if (oldValue === visibilityOptions.PRIVATE) { + // if changing away from private, make enabled features more permissive + if (this.issuesAccessLevel > 0) this.issuesAccessLevel = 20; + if (this.repositoryAccessLevel > 0) this.repositoryAccessLevel = 20; + if (this.mergeRequestsAccessLevel > 0) this.mergeRequestsAccessLevel = 20; + if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20; + if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20; + if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20; + this.highlightChanges(); + } + }, + + repositoryAccessLevel(value, oldValue) { + if (value < oldValue) { + // sub-features cannot have more premissive access level + this.mergeRequestsAccessLevel = Math.min(this.mergeRequestsAccessLevel, value); + this.buildsAccessLevel = Math.min(this.buildsAccessLevel, value); - if (value === 0) { - this.containerRegistryEnabled = false; - this.lfsEnabled = false; + if (value === 0) { + this.containerRegistryEnabled = false; + this.lfsEnabled = false; + } + } else if (oldValue === 0) { + this.mergeRequestsAccessLevel = value; + this.buildsAccessLevel = value; + this.containerRegistryEnabled = true; + this.lfsEnabled = true; } - } else if (oldValue === 0) { - this.mergeRequestsAccessLevel = value; - this.buildsAccessLevel = value; - this.containerRegistryEnabled = true; - this.lfsEnabled = true; - } - }, + }, - issuesAccessLevel(value, oldValue) { - if (value === 0) toggleHiddenClassBySelector('.issues-feature', true); - else if (oldValue === 0) toggleHiddenClassBySelector('.issues-feature', false); - }, + issuesAccessLevel(value, oldValue) { + if (value === 0) toggleHiddenClassBySelector('.issues-feature', true); + else if (oldValue === 0) toggleHiddenClassBySelector('.issues-feature', false); + }, - mergeRequestsAccessLevel(value, oldValue) { - if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true); - else if (oldValue === 0) toggleHiddenClassBySelector('.merge-requests-feature', false); - }, + mergeRequestsAccessLevel(value, oldValue) { + if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true); + else if (oldValue === 0) toggleHiddenClassBySelector('.merge-requests-feature', false); + }, - buildsAccessLevel(value, oldValue) { - if (value === 0) toggleHiddenClassBySelector('.builds-feature', true); - else if (oldValue === 0) toggleHiddenClassBySelector('.builds-feature', false); + buildsAccessLevel(value, oldValue) { + if (value === 0) toggleHiddenClassBySelector('.builds-feature', true); + else if (oldValue === 0) toggleHiddenClassBySelector('.builds-feature', false); + }, }, - }, -}; + methods: { + highlightChanges() { + this.highlightChangesClass = true; + this.$nextTick(() => { + this.highlightChangesClass = false; + }); + }, + + visibilityAllowed(option) { + return this.allowedVisibilityOptions.includes(option); + }, + }, + }; </script> <template> @@ -203,22 +202,36 @@ export default { Public </option> </select> - <i aria-hidden="true" data-hidden="true" class="fa fa-chevron-down"></i> + <i + aria-hidden="true" + data-hidden="true" + class="fa fa-chevron-down" + > + </i> </div> </div> <span class="help-block">{{ visibilityLevelDescription }}</span> - <label v-if="visibilityLevel !== visibilityOptions.PUBLIC" class="request-access"> + <label + v-if="visibilityLevel !== visibilityOptions.PUBLIC" + class="request-access" + > <input type="hidden" name="project[request_access_enabled]" :value="requestAccessEnabled" /> - <input type="checkbox" v-model="requestAccessEnabled" /> + <input + type="checkbox" + v-model="requestAccessEnabled" + /> Allow users to request access </label> </project-setting-row> </div> - <div class="project-feature-settings" :class="{ 'highlight-changes': highlightChangesClass }"> + <div + class="project-feature-settings" + :class="{ 'highlight-changes': highlightChangesClass }" + > <project-setting-row label="Issues" help-text="Lightweight issue tracking system for this project" @@ -248,7 +261,7 @@ export default { name="project[project_feature_attributes][merge_requests_access_level]" :options="repoFeatureAccessLevelOptions" v-model="mergeRequestsAccessLevel" - :disabledInput="!repositoryEnabled" + :disabled-input="!repositoryEnabled" /> </project-setting-row> <project-setting-row @@ -259,7 +272,7 @@ export default { name="project[project_feature_attributes][builds_access_level]" :options="repoFeatureAccessLevelOptions" v-model="buildsAccessLevel" - :disabledInput="!repositoryEnabled" + :disabled-input="!repositoryEnabled" /> </project-setting-row> <project-setting-row @@ -271,7 +284,7 @@ export default { <project-feature-toggle name="project[container_registry_enabled]" v-model="containerRegistryEnabled" - :disabledInput="!repositoryEnabled" + :disabled-input="!repositoryEnabled" /> </project-setting-row> <project-setting-row @@ -283,7 +296,7 @@ export default { <project-feature-toggle name="project[lfs_enabled]" v-model="lfsEnabled" - :disabledInput="!repositoryEnabled" + :disabled-input="!repositoryEnabled" /> </project-setting-row> </div> diff --git a/app/assets/javascripts/projects_dropdown/components/app.vue b/app/assets/javascripts/projects_dropdown/components/app.vue index 7606605be32..34a60dd574b 100644 --- a/app/assets/javascripts/projects_dropdown/components/app.vue +++ b/app/assets/javascripts/projects_dropdown/components/app.vue @@ -47,6 +47,22 @@ export default { return this.store.getSearchedProjects(); }, }, + created() { + if (this.currentProject.id) { + this.logCurrentProjectAccess(); + } + + eventHub.$on('dropdownOpen', this.fetchFrequentProjects); + eventHub.$on('searchProjects', this.fetchSearchedProjects); + eventHub.$on('searchCleared', this.handleSearchClear); + eventHub.$on('searchFailed', this.handleSearchFailure); + }, + beforeDestroy() { + eventHub.$off('dropdownOpen', this.fetchFrequentProjects); + eventHub.$off('searchProjects', this.fetchSearchedProjects); + eventHub.$off('searchCleared', this.handleSearchClear); + eventHub.$off('searchFailed', this.handleSearchFailure); + }, methods: { toggleFrequentProjectsList(state) { this.isLoadingProjects = !state; @@ -108,22 +124,6 @@ export default { this.toggleSearchProjectsList(true); }, }, - created() { - if (this.currentProject.id) { - this.logCurrentProjectAccess(); - } - - eventHub.$on('dropdownOpen', this.fetchFrequentProjects); - eventHub.$on('searchProjects', this.fetchSearchedProjects); - eventHub.$on('searchCleared', this.handleSearchClear); - eventHub.$on('searchFailed', this.handleSearchFailure); - }, - beforeDestroy() { - eventHub.$off('dropdownOpen', this.fetchFrequentProjects); - eventHub.$off('searchProjects', this.fetchSearchedProjects); - eventHub.$off('searchCleared', this.handleSearchClear); - eventHub.$off('searchFailed', this.handleSearchFailure); - }, }; </script> diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue index 093554cd0bc..246dbeaaded 100644 --- a/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue +++ b/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue @@ -1,32 +1,32 @@ <script> -import { s__ } from '../../locale'; -import projectsListItem from './projects_list_item.vue'; + import { s__ } from '../../locale'; + import projectsListItem from './projects_list_item.vue'; -export default { - components: { - projectsListItem, - }, - props: { - projects: { - type: Array, - required: true, + export default { + components: { + projectsListItem, }, - localStorageFailed: { - type: Boolean, - required: true, + props: { + projects: { + type: Array, + required: true, + }, + localStorageFailed: { + type: Boolean, + required: true, + }, }, - }, - computed: { - isListEmpty() { - return this.projects.length === 0; + computed: { + isListEmpty() { + return this.projects.length === 0; + }, + listEmptyMessage() { + return this.localStorageFailed ? + s__('ProjectsDropdown|This feature requires browser localStorage support') : + s__('ProjectsDropdown|Projects you visit often will appear here'); + }, }, - listEmptyMessage() { - return this.localStorageFailed ? - s__('ProjectsDropdown|This feature requires browser localStorage support') : - s__('ProjectsDropdown|Projects you visit often will appear here'); - }, - }, -}; + }; </script> <template> @@ -40,7 +40,7 @@ export default { class="section-empty" v-if="isListEmpty" > - {{listEmptyMessage}} + {{ listEmptyMessage }} </li> <projects-list-item v-else diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue index d482a7025de..8edb19572c3 100644 --- a/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue +++ b/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue @@ -1,76 +1,76 @@ <script> -import identicon from '../../vue_shared/components/identicon.vue'; + import identicon from '../../vue_shared/components/identicon.vue'; -export default { - components: { - identicon, - }, - props: { - matcher: { - type: String, - required: false, + export default { + components: { + identicon, }, - projectId: { - type: Number, - required: true, - }, - projectName: { - type: String, - required: true, - }, - namespace: { - type: String, - required: true, - }, - webUrl: { - type: String, - required: true, - }, - avatarUrl: { - required: true, - validator(value) { - return value === null || typeof value === 'string'; + props: { + matcher: { + type: String, + required: false, + }, + projectId: { + type: Number, + required: true, + }, + projectName: { + type: String, + required: true, + }, + namespace: { + type: String, + required: true, + }, + webUrl: { + type: String, + required: true, + }, + avatarUrl: { + required: true, + validator(value) { + return value === null || typeof value === 'string'; + }, }, }, - }, - computed: { - hasAvatar() { - return this.avatarUrl !== null; - }, - highlightedProjectName() { - if (this.matcher) { - const matcherRegEx = new RegExp(this.matcher, 'gi'); - const matches = this.projectName.match(matcherRegEx); + computed: { + hasAvatar() { + return this.avatarUrl !== null; + }, + highlightedProjectName() { + if (this.matcher) { + const matcherRegEx = new RegExp(this.matcher, 'gi'); + const matches = this.projectName.match(matcherRegEx); - if (matches && matches.length > 0) { - return this.projectName.replace(matches[0], `<b>${matches[0]}</b>`); + if (matches && matches.length > 0) { + return this.projectName.replace(matches[0], `<b>${matches[0]}</b>`); + } } - } - return this.projectName; - }, - /** - * Smartly truncates project namespace by doing two things; - * 1. Only include Group names in path by removing project name - * 2. Only include first and last group names in the path - * when namespace has more than 2 groups present - * - * First part (removal of project name from namespace) can be - * done from backend but doing so involves migration of - * existing project namespaces which is not wise thing to do. - */ - truncatedNamespace() { - const namespaceArr = this.namespace.split(' / '); - namespaceArr.splice(-1, 1); - let namespace = namespaceArr.join(' / '); + return this.projectName; + }, + /** + * Smartly truncates project namespace by doing two things; + * 1. Only include Group names in path by removing project name + * 2. Only include first and last group names in the path + * when namespace has more than 2 groups present + * + * First part (removal of project name from namespace) can be + * done from backend but doing so involves migration of + * existing project namespaces which is not wise thing to do. + */ + truncatedNamespace() { + const namespaceArr = this.namespace.split(' / '); + namespaceArr.splice(-1, 1); + let namespace = namespaceArr.join(' / '); - if (namespaceArr.length > 2) { - namespace = `${namespaceArr[0]} / ... / ${namespaceArr.pop()}`; - } + if (namespaceArr.length > 2) { + namespace = `${namespaceArr[0]} / ... / ${namespaceArr.pop()}`; + } - return namespace; + return namespace; + }, }, - }, -}; + }; </script> <template> @@ -92,7 +92,7 @@ export default { <identicon v-else size-class="s32" - :entity-id=projectId + :entity-id="projectId" :entity-name="projectName" /> </div> @@ -108,7 +108,7 @@ export default { <div class="project-namespace" :title="namespace" - >{{truncatedNamespace}}</div> + >{{ truncatedNamespace }}</div> </div> </a> </li> diff --git a/app/assets/javascripts/projects_dropdown/components/search.vue b/app/assets/javascripts/projects_dropdown/components/search.vue index 53bc76d0f2d..3bfe4cb0407 100644 --- a/app/assets/javascripts/projects_dropdown/components/search.vue +++ b/app/assets/javascripts/projects_dropdown/components/search.vue @@ -1,47 +1,47 @@ <script> -import _ from 'underscore'; -import eventHub from '../event_hub'; + import _ from 'underscore'; + import eventHub from '../event_hub'; -export default { - data() { - return { - searchQuery: '', - }; - }, - watch: { - searchQuery() { - this.handleInput(); + export default { + data() { + return { + searchQuery: '', + }; }, - }, - methods: { - setFocus() { - this.$refs.search.focus(); + watch: { + searchQuery() { + this.handleInput(); + }, }, - emitSearchEvents() { - if (this.searchQuery) { - eventHub.$emit('searchProjects', this.searchQuery); - } else { - eventHub.$emit('searchCleared'); - } + mounted() { + eventHub.$on('dropdownOpen', this.setFocus); }, - /** - * Callback function within _.debounce is intentionally - * kept as ES5 `function() {}` instead of ES6 `() => {}` - * as it otherwise messes up function context - * and component reference is no longer accessible via `this` - */ - // eslint-disable-next-line func-names - handleInput: _.debounce(function () { - this.emitSearchEvents(); - }, 500), - }, - mounted() { - eventHub.$on('dropdownOpen', this.setFocus); - }, - beforeDestroy() { - eventHub.$off('dropdownOpen', this.setFocus); - }, -}; + beforeDestroy() { + eventHub.$off('dropdownOpen', this.setFocus); + }, + methods: { + setFocus() { + this.$refs.search.focus(); + }, + emitSearchEvents() { + if (this.searchQuery) { + eventHub.$emit('searchProjects', this.searchQuery); + } else { + eventHub.$emit('searchCleared'); + } + }, + /** + * Callback function within _.debounce is intentionally + * kept as ES5 `function() {}` instead of ES6 `() => {}` + * as it otherwise messes up function context + * and component reference is no longer accessible via `this` + */ + // eslint-disable-next-line func-names + handleInput: _.debounce(function () { + this.emitSearchEvents(); + }, 500), + }, + }; </script> <template> diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue index 2d8ca443ea7..2949545886b 100644 --- a/app/assets/javascripts/registry/components/app.vue +++ b/app/assets/javascripts/registry/components/app.vue @@ -1,14 +1,17 @@ <script> - /* globals Flash */ import { mapGetters, mapActions } from 'vuex'; - import '../../flash'; + import Flash from '../../flash'; import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import store from '../stores'; import collapsibleContainer from './collapsible_container.vue'; import { errorMessages, errorMessagesTypes } from '../constants'; export default { - name: 'registryListApp', + name: 'RegistryListApp', + components: { + collapsibleContainer, + loadingIcon, + }, props: { endpoint: { type: String, @@ -16,22 +19,12 @@ }, }, store, - components: { - collapsibleContainer, - loadingIcon, - }, computed: { ...mapGetters([ 'isLoading', 'repos', ]), }, - methods: { - ...mapActions([ - 'setMainEndpoint', - 'fetchRepos', - ]), - }, created() { this.setMainEndpoint(this.endpoint); }, @@ -39,6 +32,12 @@ this.fetchRepos() .catch(() => Flash(errorMessages[errorMessagesTypes.FETCH_REPOS])); }, + methods: { + ...mapActions([ + 'setMainEndpoint', + 'fetchRepos', + ]), + }, }; </script> <template> @@ -46,17 +45,17 @@ <loading-icon v-if="isLoading" size="3" - /> + /> <collapsible-container v-else-if="!isLoading && repos.length" v-for="(item, index) in repos" :key="index" :repo="item" - /> + /> <p v-else-if="!isLoading && !repos.length"> - {{__("No container images stored for this project. Add one by following the instructions above.")}} + {{ __("No container images stored for this project. Add one by following the instructions above.") }} </p> </div> </template> diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue index ac1c3ec253c..b4906ba4ee5 100644 --- a/app/assets/javascripts/registry/components/collapsible_container.vue +++ b/app/assets/javascripts/registry/components/collapsible_container.vue @@ -1,7 +1,6 @@ <script> - /* globals Flash */ import { mapActions } from 'vuex'; - import '../../flash'; + import Flash from '../../flash'; import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import tooltip from '../../vue_shared/directives/tooltip'; @@ -9,13 +8,7 @@ import { errorMessages, errorMessagesTypes } from '../constants'; export default { - name: 'collapsibeContainerRegisty', - props: { - repo: { - type: Object, - required: true, - }, - }, + name: 'CollapsibeContainerRegisty', components: { clipboardButton, loadingIcon, @@ -24,6 +17,12 @@ directives: { tooltip, }, + props: { + repo: { + type: Object, + required: true, + }, + }, data() { return { isOpen: false, @@ -65,28 +64,29 @@ <template> <div class="container-image"> - <div - class="container-image-head"> + <div class="container-image-head"> <button type="button" @click="toggleRepo" - class="js-toggle-repo btn-link"> + class="js-toggle-repo btn-link" + > <i class="fa" :class="{ 'fa-chevron-right': !isOpen, 'fa-chevron-up': isOpen, }" - aria-hidden="true"> + aria-hidden="true" + > </i> - {{repo.name}} + {{ repo.name }} </button> <clipboard-button v-if="repo.location" :text="clipboardText" :title="repo.location" - /> + /> <div class="controls hidden-xs pull-right"> <button @@ -96,35 +96,38 @@ :title="s__('ContainerRegistry|Remove repository')" :aria-label="s__('ContainerRegistry|Remove repository')" v-tooltip - @click="handleDeleteRepository"> + @click="handleDeleteRepository" + > <i class="fa fa-trash" - aria-hidden="true"> + aria-hidden="true" + > </i> </button> </div> - </div> <loading-icon v-if="repo.isLoading" class="append-bottom-20" size="2" - /> + /> <div v-else-if="!repo.isLoading && isOpen" - class="container-image-tags"> + class="container-image-tags" + > <table-registry v-if="repo.list.length" :repo="repo" - /> + /> <div v-else - class="nothing-here-block"> - {{s__("ContainerRegistry|No tags in Container Registry for this container image.")}} + class="nothing-here-block" + > + {{ s__("ContainerRegistry|No tags in Container Registry for this container image.") }} </div> </div> </div> diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue index 14d43e135fe..bef850eddc0 100644 --- a/app/assets/javascripts/registry/components/table_registry.vue +++ b/app/assets/javascripts/registry/components/table_registry.vue @@ -1,8 +1,7 @@ <script> - /* globals Flash */ import { mapActions } from 'vuex'; import { n__ } from '../../locale'; - import '../../flash'; + import Flash from '../../flash'; import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import tablePagination from '../../vue_shared/components/table_pagination.vue'; import tooltip from '../../vue_shared/directives/tooltip'; @@ -11,21 +10,21 @@ import { numberToHumanSize } from '../../lib/utils/number_utils'; export default { - props: { - repo: { - type: Object, - required: true, - }, - }, components: { clipboardButton, tablePagination, }, + directives: { + tooltip, + }, mixins: [ timeagoMixin, ], - directives: { - tooltip, + props: { + repo: { + type: Object, + required: true, + }, }, computed: { shouldRenderPagination() { @@ -68,75 +67,78 @@ }; </script> <template> -<div> - <table class="table tags"> - <thead> - <tr> - <th>{{s__('ContainerRegistry|Tag')}}</th> - <th>{{s__('ContainerRegistry|Tag ID')}}</th> - <th>{{s__("ContainerRegistry|Size")}}</th> - <th>{{s__("ContainerRegistry|Created")}}</th> - <th></th> - </tr> - </thead> - <tbody> - <tr - v-for="(item, i) in repo.list" - :key="i"> - <td> + <div> + <table class="table tags"> + <thead> + <tr> + <th>{{ s__('ContainerRegistry|Tag') }}</th> + <th>{{ s__('ContainerRegistry|Tag ID') }}</th> + <th>{{ s__("ContainerRegistry|Size") }}</th> + <th>{{ s__("ContainerRegistry|Created") }}</th> + <th></th> + </tr> + </thead> + <tbody> + <tr + v-for="(item, i) in repo.list" + :key="i"> + <td> - {{item.tag}} + {{ item.tag }} - <clipboard-button - v-if="item.location" - :title="item.location" - :text="clipboardText(item.location)" + <clipboard-button + v-if="item.location" + :title="item.location" + :text="clipboardText(item.location)" /> - </td> - <td> - <span - v-tooltip - :title="item.revision" - data-placement="bottom"> - {{item.shortRevision}} + </td> + <td> + <span + v-tooltip + :title="item.revision" + data-placement="bottom" + > + {{ item.shortRevision }} </span> - </td> - <td> - {{formatSize(item.size)}} - <template v-if="item.size && item.layers"> - · - </template> - {{layers(item)}} - </td> + </td> + <td> + {{ formatSize(item.size) }} + <template v-if="item.size && item.layers"> + · + </template> + {{ layers(item) }} + </td> - <td> - {{timeFormated(item.createdAt)}} - </td> + <td> + {{ timeFormated(item.createdAt) }} + </td> - <td class="content"> - <button - v-if="item.canDelete" - type="button" - class="js-delete-registry btn btn-danger hidden-xs pull-right" - :title="s__('ContainerRegistry|Remove tag')" - :aria-label="s__('ContainerRegistry|Remove tag')" - data-container="body" - v-tooltip - @click="handleDeleteRegistry(item)"> - <i - class="fa fa-trash" - aria-hidden="true"> - </i> - </button> - </td> - </tr> - </tbody> - </table> + <td class="content"> + <button + v-if="item.canDelete" + type="button" + class="js-delete-registry btn btn-danger hidden-xs pull-right" + :title="s__('ContainerRegistry|Remove tag')" + :aria-label="s__('ContainerRegistry|Remove tag')" + data-container="body" + v-tooltip + @click="handleDeleteRegistry(item)" + > + <i + class="fa fa-trash" + aria-hidden="true" + > + </i> + </button> + </td> + </tr> + </tbody> + </table> - <table-pagination - v-if="shouldRenderPagination" - :change="onPageChange" - :page-info="repo.pagination" + <table-pagination + v-if="shouldRenderPagination" + :change="onPageChange" + :page-info="repo.pagination" /> -</div> + </div> </template> diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue index 80927529ffe..839f9ec88b9 100644 --- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue @@ -1,51 +1,51 @@ <script> -import Flash from '../../../flash'; -import editForm from './edit_form.vue'; -import Icon from '../../../vue_shared/components/icon.vue'; + import Flash from '../../../flash'; + import editForm from './edit_form.vue'; + import Icon from '../../../vue_shared/components/icon.vue'; -export default { - components: { - editForm, - Icon, - }, - props: { - isConfidential: { - required: true, - type: Boolean, + export default { + components: { + editForm, + Icon, }, - isEditable: { - required: true, - type: Boolean, + props: { + isConfidential: { + required: true, + type: Boolean, + }, + isEditable: { + required: true, + type: Boolean, + }, + service: { + required: true, + type: Object, + }, }, - service: { - required: true, - type: Object, + data() { + return { + edit: false, + }; }, - }, - data() { - return { - edit: false, - }; - }, - computed: { - confidentialityIcon() { - return this.isConfidential ? 'eye-slash' : 'eye'; + computed: { + confidentialityIcon() { + return this.isConfidential ? 'eye-slash' : 'eye'; + }, }, - }, - methods: { - toggleForm() { - this.edit = !this.edit; + methods: { + toggleForm() { + this.edit = !this.edit; + }, + updateConfidentialAttribute(confidential) { + this.service.update('issue', { confidential }) + .then(() => location.reload()) + .catch(() => { + Flash(`Something went wrong trying to + change the confidentiality of this issue`); + }); + }, }, - updateConfidentialAttribute(confidential) { - this.service.update('issue', { confidential }) - .then(() => location.reload()) - .catch(() => { - Flash(`Something went wrong trying to -change the confidentiality of this issue`); - }); - }, - }, -}; + }; </script> <template> @@ -54,8 +54,8 @@ change the confidentiality of this issue`); <icon :name="confidentialityIcon" :size="16" - aria-hidden="true"> - </icon> + aria-hidden="true" + /> </div> <div class="title hide-collapsed"> Confidentiality @@ -75,22 +75,26 @@ change the confidentiality of this issue`); :is-confidential="isConfidential" :update-confidential-attribute="updateConfidentialAttribute" /> - <div v-if="!isConfidential" class="no-value sidebar-item-value"> + <div + v-if="!isConfidential" + class="no-value sidebar-item-value"> <icon name="eye" :size="16" aria-hidden="true" - class="sidebar-item-icon inline"> - </icon> + class="sidebar-item-icon inline" + /> Not confidential </div> - <div v-else class="value sidebar-item-value hide-collapsed"> + <div + v-else + class="value sidebar-item-value hide-collapsed"> <icon name="eye-slash" :size="16" aria-hidden="true" - class="sidebar-item-icon inline is-active"> - </icon> + class="sidebar-item-icon inline is-active" + /> This issue is confidential </div> </div> diff --git a/app/assets/javascripts/sidebar/components/confidential/edit_form.vue b/app/assets/javascripts/sidebar/components/confidential/edit_form.vue index dd17b5abd46..6a81235a1a7 100644 --- a/app/assets/javascripts/sidebar/components/confidential/edit_form.vue +++ b/app/assets/javascripts/sidebar/components/confidential/edit_form.vue @@ -1,26 +1,25 @@ <script> -import editFormButtons from './edit_form_buttons.vue'; + import editFormButtons from './edit_form_buttons.vue'; -export default { - props: { - isConfidential: { - required: true, - type: Boolean, + export default { + components: { + editFormButtons, }, - toggleForm: { - required: true, - type: Function, + props: { + isConfidential: { + required: true, + type: Boolean, + }, + toggleForm: { + required: true, + type: Function, + }, + updateConfidentialAttribute: { + required: true, + type: Function, + }, }, - updateConfidentialAttribute: { - required: true, - type: Function, - }, - }, - - components: { - editFormButtons, - }, -}; + }; </script> <template> diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form.vue b/app/assets/javascripts/sidebar/components/lock/edit_form.vue index 242e826d471..e7a87636aa7 100644 --- a/app/assets/javascripts/sidebar/components/lock/edit_form.vue +++ b/app/assets/javascripts/sidebar/components/lock/edit_form.vue @@ -1,45 +1,47 @@ <script> -import editFormButtons from './edit_form_buttons.vue'; -import issuableMixin from '../../../vue_shared/mixins/issuable'; + import editFormButtons from './edit_form_buttons.vue'; + import issuableMixin from '../../../vue_shared/mixins/issuable'; -export default { - props: { - isLocked: { - required: true, - type: Boolean, + export default { + components: { + editFormButtons, }, - - toggleForm: { - required: true, - type: Function, - }, - - updateLockedAttribute: { - required: true, - type: Function, + mixins: [ + issuableMixin, + ], + props: { + isLocked: { + required: true, + type: Boolean, + }, + + toggleForm: { + required: true, + type: Function, + }, + + updateLockedAttribute: { + required: true, + type: Function, + }, }, - }, - - mixins: [ - issuableMixin, - ], - - components: { - editFormButtons, - }, -}; + }; </script> <template> <div class="dropdown open"> <div class="dropdown-menu sidebar-item-warning-message"> - <p class="text" v-if="isLocked"> + <p + class="text" + v-if="isLocked"> Unlock this {{ issuableDisplayName }}? <strong>Everyone</strong> will be able to comment. </p> - <p class="text" v-else> + <p + class="text" + v-else> Lock this {{ issuableDisplayName }}? Only <strong>project members</strong> diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue index bded18996eb..02876a6c175 100644 --- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue @@ -1,64 +1,63 @@ <script> -/* global Flash */ -import editForm from './edit_form.vue'; -import issuableMixin from '../../../vue_shared/mixins/issuable'; -import Icon from '../../../vue_shared/components/icon.vue'; + import Flash from '../../../flash'; + import editForm from './edit_form.vue'; + import issuableMixin from '../../../vue_shared/mixins/issuable'; + import Icon from '../../../vue_shared/components/icon.vue'; -export default { - props: { - isLocked: { - required: true, - type: Boolean, + export default { + components: { + editForm, + Icon, }, + mixins: [ + issuableMixin, + ], - isEditable: { - required: true, - type: Boolean, - }, - - mediator: { - required: true, - type: Object, - validator(mediatorObject) { - return mediatorObject.service && mediatorObject.service.update && mediatorObject.store; + props: { + isLocked: { + required: true, + type: Boolean, }, - }, - }, - - mixins: [ - issuableMixin, - ], - components: { - editForm, - Icon, - }, + isEditable: { + required: true, + type: Boolean, + }, - computed: { - lockIcon() { - return this.isLocked ? 'lock' : 'lock-open'; + mediator: { + required: true, + type: Object, + validator(mediatorObject) { + return mediatorObject.service && mediatorObject.service.update && mediatorObject.store; + }, + }, }, - isLockDialogOpen() { - return this.mediator.store.isLockDialogOpen; - }, - }, + computed: { + lockIcon() { + return this.isLocked ? 'lock' : 'lock-open'; + }, - methods: { - toggleForm() { - this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen; + isLockDialogOpen() { + return this.mediator.store.isLockDialogOpen; + }, }, - updateLockedAttribute(locked) { - this.mediator.service.update(this.issuableType, { - discussion_locked: locked, - }) - .then(() => location.reload()) - .catch(() => Flash(this.__(`Something went wrong trying to -change the locked state of this ${this.issuableDisplayName}`))); + methods: { + toggleForm() { + this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen; + }, + + updateLockedAttribute(locked) { + this.mediator.service.update(this.issuableType, { + discussion_locked: locked, + }) + .then(() => location.reload()) + .catch(() => Flash(this.__(`Something went wrong trying to + change the locked state of this ${this.issuableDisplayName}`))); + }, }, - }, -}; + }; </script> <template> @@ -68,8 +67,8 @@ change the locked state of this ${this.issuableDisplayName}`))); :name="lockIcon" :size="16" aria-hidden="true" - class="sidebar-item-icon is-active"> - </icon> + class="sidebar-item-icon is-active" + /> </div> <div class="title hide-collapsed"> @@ -101,8 +100,8 @@ change the locked state of this ${this.issuableDisplayName}`))); name="lock" :size="16" aria-hidden="true" - class="sidebar-item-icon inline is-active"> - </icon> + class="sidebar-item-icon inline is-active" + /> {{ __('Locked') }} </div> @@ -114,8 +113,8 @@ change the locked state of this ${this.issuableDisplayName}`))); name="lock-open" :size="16" aria-hidden="true" - class="sidebar-item-icon inline"> - </icon> + class="sidebar-item-icon inline" + /> {{ __('Unlocked') }} </div> </div> diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue index b8510a6ce3a..006a6d2905d 100644 --- a/app/assets/javascripts/sidebar/components/participants/participants.vue +++ b/app/assets/javascripts/sidebar/components/participants/participants.vue @@ -1,73 +1,73 @@ <script> -import { __, n__, sprintf } from '../../../locale'; -import loadingIcon from '../../../vue_shared/components/loading_icon.vue'; -import userAvatarImage from '../../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import { __, n__, sprintf } from '../../../locale'; + import loadingIcon from '../../../vue_shared/components/loading_icon.vue'; + import userAvatarImage from '../../../vue_shared/components/user_avatar/user_avatar_image.vue'; -export default { - props: { - loading: { - type: Boolean, - required: false, - default: false, + export default { + components: { + loadingIcon, + userAvatarImage, }, - participants: { - type: Array, - required: false, - default: () => [], + props: { + loading: { + type: Boolean, + required: false, + default: false, + }, + participants: { + type: Array, + required: false, + default: () => [], + }, + numberOfLessParticipants: { + type: Number, + required: false, + default: 7, + }, }, - numberOfLessParticipants: { - type: Number, - required: false, - default: 7, + data() { + return { + isShowingMoreParticipants: false, + }; }, - }, - data() { - return { - isShowingMoreParticipants: false, - }; - }, - components: { - loadingIcon, - userAvatarImage, - }, - computed: { - lessParticipants() { - return this.participants.slice(0, this.numberOfLessParticipants); - }, - visibleParticipants() { - return this.isShowingMoreParticipants ? this.participants : this.lessParticipants; - }, - hasMoreParticipants() { - return this.participants.length > this.numberOfLessParticipants; - }, - toggleLabel() { - let label = ''; - if (this.isShowingMoreParticipants) { - label = __('- show less'); - } else { - label = sprintf(__('+ %{moreCount} more'), { - moreCount: this.participants.length - this.numberOfLessParticipants, - }); - } + computed: { + lessParticipants() { + return this.participants.slice(0, this.numberOfLessParticipants); + }, + visibleParticipants() { + return this.isShowingMoreParticipants ? this.participants : this.lessParticipants; + }, + hasMoreParticipants() { + return this.participants.length > this.numberOfLessParticipants; + }, + toggleLabel() { + let label = ''; + if (this.isShowingMoreParticipants) { + label = __('- show less'); + } else { + label = sprintf(__('+ %{moreCount} more'), { + moreCount: this.participants.length - this.numberOfLessParticipants, + }); + } - return label; - }, - participantLabel() { - return sprintf( - n__('%{count} participant', '%{count} participants', this.participants.length), - { count: this.loading ? '' : this.participantCount }, - ); - }, - participantCount() { - return this.participants.length; + return label; + }, + participantLabel() { + return sprintf( + n__('%{count} participant', '%{count} participants', this.participants.length), + { count: this.loading ? '' : this.participantCount }, + ); + }, + participantCount() { + return this.participants.length; + }, }, - }, - methods: { - toggleMoreParticipants() { - this.isShowingMoreParticipants = !this.isShowingMoreParticipants; + methods: { + toggleMoreParticipants() { + this.isShowingMoreParticipants = !this.isShowingMoreParticipants; + }, }, - }, -}; + }; </script> <template> @@ -75,14 +75,17 @@ export default { <div class="sidebar-collapsed-icon"> <i class="fa fa-users" - aria-hidden="true"> + aria-hidden="true" + > </i> <loading-icon v-if="loading" - class="js-participants-collapsed-loading-icon" /> + class="js-participants-collapsed-loading-icon" + /> <span v-else - class="js-participants-collapsed-count"> + class="js-participants-collapsed-count" + > {{ participantCount }} </span> </div> @@ -90,34 +93,40 @@ export default { <loading-icon v-if="loading" :inline="true" - class="js-participants-expanded-loading-icon" /> + class="js-participants-expanded-loading-icon" + /> {{ participantLabel }} </div> <div class="participants-list hide-collapsed"> <div v-for="participant in visibleParticipants" :key="participant.id" - class="participants-author js-participants-author"> + class="participants-author js-participants-author" + > <a class="author_link" - :href="participant.web_url"> + :href="participant.web_url" + > <user-avatar-image :lazy="true" :img-src="participant.avatar_url" css-classes="avatar-inline" :size="24" :tooltip-text="participant.name" - tooltip-placement="bottom" /> + tooltip-placement="bottom" + /> </a> </div> </div> <div v-if="hasMoreParticipants" - class="participants-more hide-collapsed"> + class="participants-more hide-collapsed" + > <button type="button" class="btn-transparent btn-blank js-toggle-participants-button" - @click="toggleMoreParticipants"> + @click="toggleMoreParticipants" + > {{ toggleLabel }} </button> </div> diff --git a/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue b/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue index 6fcd2f95309..5c1ead1a8ac 100644 --- a/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue +++ b/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue @@ -1,23 +1,23 @@ <script> -import Store from '../../stores/sidebar_store'; -import participants from './participants.vue'; + import Store from '../../stores/sidebar_store'; + import participants from './participants.vue'; -export default { - data() { - return { - store: new Store(), - }; - }, - props: { - mediator: { - type: Object, - required: true, + export default { + components: { + participants, }, - }, - components: { - participants, - }, -}; + props: { + mediator: { + type: Object, + required: true, + }, + }, + data() { + return { + store: new Store(), + }; + }, + }; </script> <template> diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue index f4bae1d3dd5..3e8cc7a6630 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue @@ -6,10 +6,8 @@ import { __ } from '../../../locale'; import subscriptions from './subscriptions.vue'; export default { - data() { - return { - store: new Store(), - }; + components: { + subscriptions, }, props: { mediator: { @@ -17,10 +15,17 @@ export default { required: true, }, }, - components: { - subscriptions, + data() { + return { + store: new Store(), + }; + }, + created() { + eventHub.$on('toggleSubscription', this.onToggleSubscription); + }, + beforeDestroy() { + eventHub.$off('toggleSubscription', this.onToggleSubscription); }, - methods: { onToggleSubscription() { this.mediator.toggleSubscription() @@ -29,14 +34,6 @@ export default { }); }, }, - - created() { - eventHub.$on('toggleSubscription', this.onToggleSubscription); - }, - - beforeDestroy() { - eventHub.$off('toggleSubscription', this.onToggleSubscription); - }, }; </script> @@ -44,6 +41,7 @@ export default { <div class="block subscriptions"> <subscriptions :loading="store.isFetching.subscriptions" - :subscribed="store.subscribed" /> + :subscribed="store.subscribed" + /> </div> </template> diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue index 940e1764f3d..6a6a8cdd05d 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue @@ -1,45 +1,47 @@ <script> -import { __ } from '../../../locale'; -import eventHub from '../../event_hub'; -import loadingButton from '../../../vue_shared/components/loading_button.vue'; + import { __ } from '../../../locale'; + import eventHub from '../../event_hub'; + import loadingButton from '../../../vue_shared/components/loading_button.vue'; -export default { - props: { - loading: { - type: Boolean, - required: false, - default: false, + export default { + components: { + loadingButton, }, - subscribed: { - type: Boolean, - required: false, + props: { + loading: { + type: Boolean, + required: false, + default: false, + }, + subscribed: { + type: Boolean, + required: false, + default: false, + }, + id: { + type: Number, + required: false, + default: 0, + }, }, - id: { - type: Number, - required: false, - }, - }, - components: { - loadingButton, - }, - computed: { - buttonLabel() { - let label; - if (this.subscribed === false) { - label = __('Subscribe'); - } else if (this.subscribed === true) { - label = __('Unsubscribe'); - } + computed: { + buttonLabel() { + let label; + if (this.subscribed === false) { + label = __('Subscribe'); + } else if (this.subscribed === true) { + label = __('Unsubscribe'); + } - return label; + return label; + }, }, - }, - methods: { - toggleSubscription() { - eventHub.$emit('toggleSubscription', this.id); + methods: { + toggleSubscription() { + eventHub.$emit('toggleSubscription', this.id); + }, }, - }, -}; + }; </script> <template> @@ -47,7 +49,8 @@ export default { <div class="sidebar-collapsed-icon"> <i class="fa fa-rss" - aria-hidden="true"> + aria-hidden="true" + > </i> </div> <span class="issuable-header-text hide-collapsed pull-left"> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue index dbc65462377..eea5a68bdc4 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue @@ -5,6 +5,11 @@ export default { name: 'MRWidgetPipeline', + components: { + pipelineStage, + ciIcon, + icon, + }, props: { pipeline: { type: Object, @@ -21,11 +26,6 @@ required: false, }, }, - components: { - pipelineStage, - ciIcon, - icon, - }, computed: { hasPipeline() { return this.pipeline && Object.keys(this.pipeline).length > 0; @@ -62,7 +62,8 @@ <template v-else-if="hasPipeline"> <a class="append-right-10" - :href="this.status.details_path"> + :href="status.details_path" + > <ci-icon :status="status" /> </a> @@ -70,33 +71,37 @@ Pipeline <a :href="pipeline.path" - class="pipeline-id"> - #{{pipeline.id}} + class="pipeline-id" + > + #{{ pipeline.id }} </a> - {{pipeline.details.status.label}} for + {{ pipeline.details.status.label }} for <a :href="pipeline.commit.commit_path" - class="commit-sha js-commit-link"> - {{pipeline.commit.short_id}}</a>. + class="commit-sha js-commit-link" + > + {{ pipeline.commit.short_id }}</a>. <span class="mr-widget-pipeline-graph"> - <span class="stage-cell"> + <span + class="stage-cell" + v-if="hasStages" + > <div - v-if="hasStages" v-for="(stage, i) in pipeline.details.stages" :key="i" - class="stage-container dropdown js-mini-pipeline-graph"> + class="stage-container dropdown js-mini-pipeline-graph" + > <pipeline-stage :stage="stage" /> </div> </span> </span> <template v-if="pipeline.coverage"> - Coverage {{pipeline.coverage}}% + Coverage {{ pipeline.coverage }}% </template> - </div> </template> </div> diff --git a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue index fc795936abf..5324d5dc797 100644 --- a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue +++ b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue @@ -23,6 +23,12 @@ */ export default { + components: { + ciIcon, + }, + directives: { + tooltip, + }, props: { status: { type: Object, @@ -34,12 +40,6 @@ default: true, }, }, - components: { - ciIcon, - }, - directives: { - tooltip, - }, computed: { cssClass() { const className = this.status.group; @@ -53,11 +53,12 @@ :href="status.details_path" :class="cssClass" v-tooltip - :title="!showText ? status.text : ''"> + :title="!showText ? status.text : ''" + > <ci-icon :status="status" /> <template v-if="showText"> - {{status.text}} + {{ status.text }} </template> </a> </template> diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue index 2a018f38366..8fea746f4de 100644 --- a/app/assets/javascripts/vue_shared/components/ci_icon.vue +++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue @@ -23,6 +23,9 @@ * - Jobs show view sidebar */ export default { + components: { + icon, + }, props: { status: { type: Object, @@ -30,10 +33,6 @@ }, }, - components: { - icon, - }, - computed: { cssClass() { const status = this.status.group; @@ -43,9 +42,7 @@ }; </script> <template> - <span - :class="cssClass"> - <icon - :name="status.icon"/> + <span :class="cssClass"> + <icon :name="status.icon" /> </span> </template> diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue index 3a7143c450e..e18852af6e9 100644 --- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue +++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue @@ -4,7 +4,7 @@ */ export default { - name: 'clipboardButton', + name: 'ClipboardButton', props: { text: { type: String, @@ -23,10 +23,12 @@ type="button" class="btn btn-transparent btn-clipboard" :data-title="title" - :data-clipboard-text="text"> - <i - aria-hidden="true" - class="fa fa-clipboard"> - </i> + :data-clipboard-text="text" + > + <i + aria-hidden="true" + class="fa fa-clipboard" + > + </i> </button> </template> diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index 59ca9a0a6d4..6d1fe7ee8ca 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -2,9 +2,16 @@ import commitIconSvg from 'icons/_icon_commit.svg'; import userAvatarLink from './user_avatar/user_avatar_link.vue'; import tooltip from '../directives/tooltip'; - import Icon from '../../vue_shared/components/icon.vue'; + import icon from '../../vue_shared/components/icon.vue'; export default { + directives: { + tooltip, + }, + components: { + userAvatarLink, + icon, + }, props: { /** * Indicates the existance of a tag. @@ -103,13 +110,6 @@ this.author.username ? `${this.author.username}'s avatar` : null; }, }, - directives: { - tooltip, - }, - components: { - userAvatarLink, - Icon, - }, created() { this.commitIconSvg = commitIconSvg; }, @@ -118,17 +118,17 @@ <template> <div class="branch-commit"> <template v-if="hasCommitRef && showBranch"> - <div - class="icon-container hidden-xs"> + <div class="icon-container hidden-xs"> <i v-if="tag" class="fa fa-tag" - aria-hidden="true"> + aria-hidden="true" + > </i> <icon v-if="!tag" - name="fork"> - </icon> + name="fork" + /> </div> <a @@ -136,25 +136,29 @@ :href="commitRef.ref_url" v-tooltip data-container="body" - :title="commitRef.name"> - {{commitRef.name}} + :title="commitRef.name" + > + {{ commitRef.name }} </a> </template> <div v-html="commitIconSvg" - class="commit-icon js-commit-icon"> + class="commit-icon js-commit-icon" + > </div> <a class="commit-sha" - :href="commitUrl"> - {{shortSha}} + :href="commitUrl" + > + {{ shortSha }} </a> <div class="commit-title flex-truncate-parent"> <span v-if="title" - class="flex-truncate-child"> + class="flex-truncate-child" + > <user-avatar-link v-if="hasAuthor" class="avatar-image-container" @@ -165,8 +169,9 @@ /> <a class="commit-row-message" - :href="commitUrl"> - {{title}} + :href="commitUrl" + > + {{ title }} </a> </span> <span v-else> diff --git a/app/assets/javascripts/vue_shared/components/file_icon.vue b/app/assets/javascripts/vue_shared/components/file_icon.vue index 65c64967fdc..c9d7c0f4999 100644 --- a/app/assets/javascripts/vue_shared/components/file_icon.vue +++ b/app/assets/javascripts/vue_shared/components/file_icon.vue @@ -16,6 +16,10 @@ */ export default { + components: { + loadingIcon, + icon, + }, props: { fileName: { type: String, @@ -52,10 +56,6 @@ default: '', }, }, - components: { - loadingIcon, - icon, - }, computed: { spriteHref() { const iconName = getIconForFile(this.fileName) || 'file'; @@ -75,9 +75,9 @@ <span> <svg :class="[iconSizeClass, cssClasses]" - v-if="!loading && !folder"> - <use - v-bind="{'xlink:href':spriteHref}"/> + v-if="!loading && !folder" + > + <use v-bind="{ 'xlink:href':spriteHref }" /> </svg> <icon v-if="!loading && folder" diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index d305bd6acdc..79d240bdbba 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -1,75 +1,74 @@ <script> -import ciIconBadge from './ci_badge_link.vue'; -import loadingIcon from './loading_icon.vue'; -import timeagoTooltip from './time_ago_tooltip.vue'; -import tooltip from '../directives/tooltip'; -import userAvatarImage from './user_avatar/user_avatar_image.vue'; - -/** - * Renders header component for job and pipeline page based on UI mockups - * - * Used in: - * - job show page - * - pipeline show page - */ -export default { - props: { - status: { - type: Object, - required: true, + import ciIconBadge from './ci_badge_link.vue'; + import loadingIcon from './loading_icon.vue'; + import timeagoTooltip from './time_ago_tooltip.vue'; + import tooltip from '../directives/tooltip'; + import userAvatarImage from './user_avatar/user_avatar_image.vue'; + + /** + * Renders header component for job and pipeline page based on UI mockups + * + * Used in: + * - job show page + * - pipeline show page + */ + export default { + directives: { + tooltip, }, - itemName: { - type: String, - required: true, - }, - itemId: { - type: Number, - required: true, - }, - time: { - type: String, - required: true, - }, - user: { - type: Object, - required: false, - default: () => ({}), - }, - actions: { - type: Array, - required: false, - default: () => [], + + components: { + ciIconBadge, + loadingIcon, + timeagoTooltip, + userAvatarImage, }, - hasSidebarButton: { - type: Boolean, - required: false, - default: false, + props: { + status: { + type: Object, + required: true, + }, + itemName: { + type: String, + required: true, + }, + itemId: { + type: Number, + required: true, + }, + time: { + type: String, + required: true, + }, + user: { + type: Object, + required: false, + default: () => ({}), + }, + actions: { + type: Array, + required: false, + default: () => [], + }, + hasSidebarButton: { + type: Boolean, + required: false, + default: false, + }, }, - }, - - directives: { - tooltip, - }, - - components: { - ciIconBadge, - loadingIcon, - timeagoTooltip, - userAvatarImage, - }, - - computed: { - userAvatarAltText() { - return `${this.user.name}'s avatar`; + + computed: { + userAvatarAltText() { + return `${this.user.name}'s avatar`; + }, }, - }, - methods: { - onClickAction(action) { - this.$emit('actionClicked', action); + methods: { + onClickAction(action) { + this.$emit('actionClicked', action); + }, }, - }, -}; + }; </script> <template> @@ -79,7 +78,7 @@ export default { <ci-icon-badge :status="status" /> <strong> - {{itemName}} #{{itemId}} + {{ itemName }} #{{ itemId }} </strong> triggered @@ -93,30 +92,35 @@ export default { v-tooltip :href="user.path" :title="user.email" - class="js-user-link commit-committer-link"> + class="js-user-link commit-committer-link" + > <user-avatar-image :img-src="user.avatar_url" :img-alt="userAvatarAltText" :tooltip-text="user.name" :img-size="24" - /> + /> - {{user.name}} + {{ user.name }} </a> </template> </section> <section class="header-action-buttons" - v-if="actions.length"> + v-if="actions.length" + > <template - v-for="action in actions"> + v-for="(action, i) in actions" + > <a v-if="action.type === 'link'" :href="action.path" - :class="action.cssClass"> - {{action.label}} + :class="action.cssClass" + :key="i" + > + {{ action.label }} </a> <a @@ -124,8 +128,10 @@ export default { :href="action.path" data-method="post" rel="nofollow" - :class="action.cssClass"> - {{action.label}} + :class="action.cssClass" + :key="i" + > + {{ action.label }} </a> <button @@ -133,12 +139,15 @@ export default { @click="onClickAction(action)" :disabled="action.isLoading" :class="action.cssClass" - type="button"> - {{action.label}} + type="button" + :key="i" + > + {{ action.label }} <i v-show="action.isLoading" class="fa fa-spin fa-spinner" - aria-hidden="true"> + aria-hidden="true" + > </i> </button> </template> @@ -147,11 +156,13 @@ export default { type="button" class="btn btn-default visible-xs-block visible-sm-block sidebar-toggle-btn js-sidebar-build-toggle js-sidebar-build-toggle-header" aria-label="Toggle Sidebar" - id="toggleSidebar"> + id="toggleSidebar" + > <i class="fa fa-angle-double-left" aria-hidden="true" - aria-labelledby="toggleSidebar"> + aria-labelledby="toggleSidebar" + > </i> </button> </section> diff --git a/app/assets/javascripts/vue_shared/components/icon.vue b/app/assets/javascripts/vue_shared/components/icon.vue index 365229ea274..6a2e05000e1 100644 --- a/app/assets/javascripts/vue_shared/components/icon.vue +++ b/app/assets/javascripts/vue_shared/components/icon.vue @@ -1,17 +1,17 @@ <script> -/* This is a re-usable vue component for rendering a svg sprite - icon + /* This is a re-usable vue component for rendering a svg sprite + icon - Sample configuration: + Sample configuration: - <icon - name="retry" - :size="32" - css-classes="top" - /> + <icon + name="retry" + :size="32" + css-classes="top" + /> -*/ + */ // only allow classes in images.scss e.g. s12 const validSizes = [8, 12, 16, 18, 24, 32, 48, 72]; @@ -80,7 +80,6 @@ :height="height" :x="x" :y="y"> - <use - v-bind="{'xlink:href':spriteHref}"/> + <use v-bind="{ 'xlink:href':spriteHref }" /> </svg> </template> diff --git a/app/assets/javascripts/vue_shared/components/identicon.vue b/app/assets/javascripts/vue_shared/components/identicon.vue index 7cf2e029cf6..0a30f467b08 100644 --- a/app/assets/javascripts/vue_shared/components/identicon.vue +++ b/app/assets/javascripts/vue_shared/components/identicon.vue @@ -46,6 +46,6 @@ export default { class="avatar identicon" :class="sizeClass" :style="identiconStyles"> - {{identiconTitle}} + {{ identiconTitle }} </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue index 564fc5029af..168799b9a58 100644 --- a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue +++ b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue @@ -1,7 +1,10 @@ <script> - import Icon from '../../../vue_shared/components/icon.vue'; + import icon from '../../../vue_shared/components/icon.vue'; export default { + components: { + icon, + }, props: { isLocked: { type: Boolean, @@ -16,10 +19,6 @@ }, }, - components: { - Icon, - }, - computed: { warningIcon() { if (this.isConfidential) return 'eye-slash'; @@ -37,12 +36,12 @@ <template> <div class="issuable-note-warning"> <icon - :name="warningIcon" - :size="16" - class="icon inline" - aria-hidden="true" - v-if="!isLockedAndConfidential"> - </icon> + :name="warningIcon" + :size="16" + class="icon inline" + aria-hidden="true" + v-if="!isLockedAndConfidential" + /> <span v-if="isLockedAndConfidential"> {{ __('This issue is confidential and locked.') }} diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue index 247943f83e6..31caa25b723 100644 --- a/app/assets/javascripts/vue_shared/components/loading_button.vue +++ b/app/assets/javascripts/vue_shared/components/loading_button.vue @@ -1,55 +1,61 @@ <script> -/* This is a re-usable vue component for rendering a button - that will probably be sending off ajax requests and need - to show the loading status by setting the `loading` option. - This can also be used for initial page load when you don't - know the action of the button yet by setting - `loading: true, label: undefined`. + /* This is a re-usable vue component for rendering a button + that will probably be sending off ajax requests and need + to show the loading status by setting the `loading` option. + This can also be used for initial page load when you don't + know the action of the button yet by setting + `loading: true, label: undefined`. - Sample configuration: + Sample configuration: - <loading-button - :loading="true" - :label="Hello" - @click="..." - /> + <loading-button + :loading="true" + :label="Hello" + @click="..." + /> -*/ + */ -import loadingIcon from './loading_icon.vue'; + import loadingIcon from './loading_icon.vue'; -export default { - props: { - loading: { - type: Boolean, - required: false, - default: false, + export default { + components: { + loadingIcon, }, - disabled: { - type: Boolean, - required: false, - default: false, + props: { + loading: { + type: Boolean, + required: false, + default: false, + }, + disabled: { + type: Boolean, + required: false, + default: false, + }, + label: { + type: String, + required: false, + default: '', + }, + containerClass: { + type: String, + required: false, + default: 'btn btn-align-content', + }, }, - label: { - type: String, - required: false, + computed: { + hasLabel() { + return this.label !== ''; + }, }, - containerClass: { - type: String, - required: false, - default: 'btn btn-align-content', + methods: { + onClick(e) { + this.$emit('click', e); + }, }, - }, - components: { - loadingIcon, - }, - methods: { - onClick(e) { - this.$emit('click', e); - }, - }, -}; + }; </script> <template> @@ -59,23 +65,23 @@ export default { :class="containerClass" :disabled="loading || disabled" > - <transition name="fade"> - <loading-icon - v-if="loading" - :inline="true" - class="js-loading-button-icon" - :class="{ - 'append-right-5': label - }" - /> - </transition> - <transition name="fade"> - <span - v-if="label" - class="js-loading-button-label" - > - {{ label }} - </span> - </transition> + <transition name="fade"> + <loading-icon + v-if="loading" + :inline="true" + class="js-loading-button-icon" + :class="{ + 'append-right-5': label + }" + /> + </transition> + <transition name="fade"> + <span + v-if="hasLabel" + class="js-loading-button-label" + > + {{ label }} + </span> + </transition> </button> </template> diff --git a/app/assets/javascripts/vue_shared/components/loading_icon.vue b/app/assets/javascripts/vue_shared/components/loading_icon.vue index 15581d5c2a0..3d2b6c44384 100644 --- a/app/assets/javascripts/vue_shared/components/loading_icon.vue +++ b/app/assets/javascripts/vue_shared/components/loading_icon.vue @@ -38,7 +38,8 @@ class="fa fa-spin fa-spinner" :class="cssClass" aria-hidden="true" - :aria-label="label"> + :aria-label="label" + > </i> </component> </template> diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 15e3d713448..1371dca0c35 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -6,6 +6,11 @@ import icon from '../icon.vue'; export default { + components: { + markdownHeader, + markdownToolbar, + icon, + }, props: { markdownPreviewPath: { type: String, @@ -24,6 +29,7 @@ quickActionsDocsPath: { type: String, required: false, + default: '', }, canAttachFile: { type: Boolean, @@ -45,17 +51,24 @@ previewMarkdown: false, }; }, - components: { - markdownHeader, - markdownToolbar, - icon, - }, computed: { shouldShowReferencedUsers() { const referencedUsersThreshold = 10; return this.referencedUsers.length >= referencedUsersThreshold; }, }, + mounted() { + /* + GLForm class handles all the toolbar buttons + */ + return new GLForm($(this.$refs['gl-form']), this.enableAutocomplete); + }, + beforeDestroy() { + const glForm = $(this.$refs['gl-form']).data('gl-form'); + if (glForm) { + glForm.destroy(); + } + }, methods: { showPreviewTab() { if (this.previewMarkdown) return; @@ -98,18 +111,6 @@ }); }, }, - mounted() { - /* - GLForm class handles all the toolbar buttons - */ - return new GLForm($(this.$refs['gl-form']), this.enableAutocomplete); - }, - beforeDestroy() { - const glForm = $(this.$refs['gl-form']).data('gl-form'); - if (glForm) { - glForm.destroy(); - } - }, }; </script> @@ -121,34 +122,39 @@ <markdown-header :preview-markdown="previewMarkdown" @preview-markdown="showPreviewTab" - @write-markdown="showWriteTab" /> + @write-markdown="showWriteTab" + /> <div class="md-write-holder" - v-show="!previewMarkdown"> + v-show="!previewMarkdown" + > <div class="zen-backdrop"> <slot name="textarea"></slot> <a class="zen-control zen-control-leave js-zen-leave" href="#" - aria-label="Enter zen mode"> + aria-label="Enter zen mode" + > <icon name="screen-normal" - :size="32"> - </icon> + :size="32" + /> </a> <markdown-toolbar :markdown-docs-path="markdownDocsPath" :quick-actions-docs-path="quickActionsDocsPath" :can-attach-file="canAttachFile" - /> + /> </div> </div> <div class="md md-preview-holder md-preview" - v-show="previewMarkdown"> + v-show="previewMarkdown" + > <div ref="markdown-preview" - v-html="markdownPreview"> + v-html="markdownPreview" + > </div> <span v-if="markdownPreviewLoading"> Loading... @@ -158,23 +164,27 @@ <div v-if="referencedCommands" v-html="referencedCommands" - class="referenced-commands"></div> + class="referenced-commands" + > + </div> <div v-if="shouldShowReferencedUsers" - class="referenced-users"> - <span> - <i - class="fa fa-exclamation-triangle" - aria-hidden="true"> - </i> - You are about to add - <strong> - <span class="js-referenced-users-count"> - {{referencedUsers.length}} - </span> - </strong> people to the discussion. Proceed with caution. - </span> - </div> + class="referenced-users" + > + <span> + <i + class="fa fa-exclamation-triangle" + aria-hidden="true" + > + </i> + You are about to add + <strong> + <span class="js-referenced-users-count"> + {{ referencedUsers.length }} + </span> + </strong> people to the discussion. Proceed with caution. + </span> + </div> </template> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 36d2d1dc164..f65eab11a27 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -4,18 +4,26 @@ import icon from '../icon.vue'; export default { + directives: { + tooltip, + }, + components: { + toolbarButton, + icon, + }, props: { previewMarkdown: { type: Boolean, required: true, }, }, - directives: { - tooltip, + mounted() { + $(document).on('markdown-preview:show.vue', this.previewMarkdownTab); + $(document).on('markdown-preview:hide.vue', this.writeMarkdownTab); }, - components: { - toolbarButton, - icon, + beforeDestroy() { + $(document).off('markdown-preview:show.vue', this.previewMarkdownTab); + $(document).off('markdown-preview:hide.vue', this.writeMarkdownTab); }, methods: { isMarkdownForm(form) { @@ -36,14 +44,6 @@ this.$emit('write-markdown'); }, }, - mounted() { - $(document).on('markdown-preview:show.vue', this.previewMarkdownTab); - $(document).on('markdown-preview:hide.vue', this.writeMarkdownTab); - }, - beforeDestroy() { - $(document).off('markdown-preview:show.vue', this.previewMarkdownTab); - $(document).off('markdown-preview:hide.vue', this.writeMarkdownTab); - }, }; </script> @@ -52,12 +52,14 @@ <ul class="nav-links clearfix"> <li class="md-header-tab" - :class="{ active: !previewMarkdown }"> + :class="{ active: !previewMarkdown }" + > <a class="js-write-link" href="#md-write-holder" tabindex="-1" - @click.prevent="writeMarkdownTab($event)"> + @click.prevent="writeMarkdownTab($event)" + > Write </a> </li> @@ -68,46 +70,55 @@ class="js-preview-link" href="#md-preview-holder" tabindex="-1" - @click.prevent="previewMarkdownTab($event)"> + @click.prevent="previewMarkdownTab($event)" + > Preview </a> </li> <li class="md-header-toolbar" - :class="{ active: !previewMarkdown }"> + :class="{ active: !previewMarkdown }" + > <toolbar-button tag="**" button-title="Add bold text" - icon="bold" /> + icon="bold" + /> <toolbar-button tag="*" button-title="Add italic text" - icon="italic" /> + icon="italic" + /> <toolbar-button tag="> " :prepend="true" button-title="Insert a quote" - icon="quote" /> + icon="quote" + /> <toolbar-button tag="`" tag-block="```" button-title="Insert code" - icon="code" /> + icon="code" + /> <toolbar-button tag="* " :prepend="true" button-title="Add a bullet list" - icon="list-bulleted" /> + icon="list-bulleted" + /> <toolbar-button tag="1. " :prepend="true" button-title="Add a numbered list" - icon="list-numbered" /> + icon="list-numbered" + /> <toolbar-button tag="* [ ] " :prepend="true" button-title="Add a task list" - icon="task-done" /> + icon="task-done" + /> <button v-tooltip aria-label="Go full screen" @@ -115,10 +126,11 @@ data-container="body" tabindex="-1" title="Go full screen" - type="button"> + type="button" + > <icon - name="screen-full"> - </icon> + name="screen-full" + /> </button> </li> </ul> diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue index ea2509d2839..c0ee88bbf72 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue @@ -8,6 +8,7 @@ quickActionsDocsPath: { type: String, required: false, + default: '', }, canAttachFile: { type: Boolean, @@ -15,32 +16,40 @@ default: true, }, }, + computed: { + hasQuickActionsDocsPath() { + return this.quickActionsDocsPath !== ''; + }, + }, }; </script> <template> <div class="comment-toolbar clearfix"> <div class="toolbar-text"> - <template v-if="!quickActionsDocsPath && markdownDocsPath"> + <template v-if="!hasQuickActionsDocsPath && markdownDocsPath"> <a :href="markdownDocsPath" target="_blank" - tabindex="-1"> + tabindex="-1" + > Markdown is supported </a> </template> - <template v-if="quickActionsDocsPath && markdownDocsPath"> - <a + <template v-if="hasQuickActionsDocsPath && markdownDocsPath"> + <a :href="markdownDocsPath" target="_blank" - tabindex="-1"> + tabindex="-1" + > Markdown </a> and - <a + <a :href="quickActionsDocsPath" target="_blank" - tabindex="-1"> + tabindex="-1" + > quick actions </a> are supported @@ -53,46 +62,58 @@ <span class="uploading-progress-container hide"> <i class="fa fa-file-image-o toolbar-button-icon" - aria-hidden="true"></i> + aria-hidden="true" + > + </i> <span class="attaching-file-message"></span> <span class="uploading-progress">0%</span> <span class="uploading-spinner"> <i class="fa fa-spinner fa-spin toolbar-button-icon" - aria-hidden="true"></i> + aria-hidden="true" + > + </i> </span> </span> <span class="uploading-error-container hide"> <span class="uploading-error-icon"> <i class="fa fa-file-image-o toolbar-button-icon" - aria-hidden="true"></i> + aria-hidden="true" + > + </i> </span> <span class="uploading-error-message"></span> <button class="retry-uploading-link" - type="button"> - Try again + type="button" + > + Try again </button> or <button class="attach-new-file markdown-selector" - type="button"> + type="button" + > attach a new file </button> </span> <button class="markdown-selector button-attach-file" tabindex="-1" - type="button"> + type="button" + > <i class="fa fa-file-image-o toolbar-button-icon" - aria-hidden="true"></i> + aria-hidden="true" + > + </i> Attach a file </button> <button class="btn btn-default btn-xs hide button-cancel-uploading-files" - type="button"> + type="button" + > Cancel </button> </span> diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue index e3e41f8f0ca..2d2d69ebeb2 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue @@ -3,6 +3,12 @@ import icon from '../icon.vue'; export default { + components: { + icon, + }, + directives: { + tooltip, + }, props: { buttonTitle: { type: String, @@ -27,12 +33,6 @@ default: false, }, }, - components: { - icon, - }, - directives: { - tooltip, - }, }; </script> @@ -47,9 +47,10 @@ :data-md-block="tagBlock" :data-md-prepend="prepend" :title="buttonTitle" - :aria-label="buttonTitle"> + :aria-label="buttonTitle" + > <icon - :name="icon"> - </icon> + :name="icon" + /> </button> </template> |