diff options
author | Chris Baumbauer <cab@cabnetworks.net> | 2018-11-02 08:39:25 -0700 |
---|---|---|
committer | Chris Baumbauer <cab@cabnetworks.net> | 2018-11-02 08:39:25 -0700 |
commit | dc078c241765cfea5f49409407b82db7296c132d (patch) | |
tree | deeabb4b302f896b60ffe8e27163a4dddf625c4b /app/assets/javascripts | |
parent | 28a9bbceb7ab5ffe2305db02052d663b1e68ab8e (diff) | |
parent | a80ee886e4b4a41ace5fb796bf920c4e395bf4dd (diff) | |
download | gitlab-ce-dc078c241765cfea5f49409407b82db7296c132d.tar.gz |
Merge branch 'master' into triggermesh-phase1-knative
Diffstat (limited to 'app/assets/javascripts')
270 files changed, 5160 insertions, 4264 deletions
diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index de4566bb119..05de970e387 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -6,10 +6,12 @@ import Pager from './pager'; import { localTimeAgo } from './lib/utils/datetime_utility'; export default class Activities { - constructor() { - Pager.init(20, true, false, data => data, this.updateTooltips); + constructor(container = '') { + this.container = container; - $('.event-filter-link').on('click', (e) => { + Pager.init(20, true, false, data => data, this.updateTooltips, this.container); + + $('.event-filter-link').on('click', e => { e.preventDefault(); this.toggleFilter(e.currentTarget); this.reloadActivities(); @@ -22,7 +24,7 @@ export default class Activities { reloadActivities() { $('.content_list').html(''); - Pager.init(20, true, false, data => data, this.updateTooltips); + Pager.init(20, true, false, data => data, this.updateTooltips, this.container); } toggleFilter(sender) { diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index 3cc89ff1955..ec27ae8c291 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -13,7 +13,7 @@ export default () => { if (editBlobForm.length) { const urlRoot = editBlobForm.data('relativeUrlRoot'); const assetsPath = editBlobForm.data('assetsPrefix'); - const filePath = editBlobForm.data('blobFilename') + const filePath = editBlobForm.data('blobFilename'); const currentAction = $('.js-file-title').data('currentAction'); const projectId = editBlobForm.data('project-id'); diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js index 75477ebb3b3..fb6e5291a61 100644 --- a/app/assets/javascripts/boards/components/board.js +++ b/app/assets/javascripts/boards/components/board.js @@ -42,7 +42,7 @@ export default Vue.extend({ required: true, }, }, - data () { + data() { return { detailIssue: boardsStore.detail, filter: boardsStore.filter, @@ -53,26 +53,28 @@ export default Vue.extend({ const { issuesSize } = this.list; return `${n__('%d issue', '%d issues', issuesSize)}`; }, + isNewIssueShown() { + return this.list.type === 'backlog' || (!this.disabled && this.list.type !== 'closed'); + }, }, watch: { filter: { handler() { this.list.page = 1; - this.list.getIssues(true) - .catch(() => { - // TODO: handle request error - }); + this.list.getIssues(true).catch(() => { + // TODO: handle request error + }); }, deep: true, - } + }, }, - mounted () { + mounted() { this.sortableOptions = getBoardSortableDefaultOptions({ disabled: this.disabled, group: 'boards', draggable: '.is-draggable', handle: '.js-board-handle', - onEnd: (e) => { + onEnd: e => { sortableEnd(); if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) { @@ -83,14 +85,15 @@ export default Vue.extend({ boardsStore.moveList(list, order); }); } - } + }, }); this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions); }, created() { if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) { - const isCollapsed = localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false'; + const isCollapsed = + localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false'; this.list.isExpanded = !isCollapsed; } @@ -104,7 +107,10 @@ export default Vue.extend({ this.list.isExpanded = !this.list.isExpanded; if (AccessorUtilities.isLocalStorageAccessSafe()) { - localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded); + localStorage.setItem( + `boards.${this.boardId}.${this.list.type}.expanded`, + this.list.isExpanded, + ); } } }, diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue index 38aaec73d7d..561a4636ef5 100644 --- a/app/assets/javascripts/boards/components/board_blank_state.vue +++ b/app/assets/javascripts/boards/components/board_blank_state.vue @@ -32,18 +32,18 @@ export default { boardsStore.state.lists = _.sortBy(boardsStore.state.lists, 'position'); // Save the labels - gl.boardService.generateDefaultLists() + gl.boardService + .generateDefaultLists() .then(res => res.data) - .then((data) => { - data.forEach((listObj) => { + .then(data => { + data.forEach(listObj => { const list = boardsStore.findList('title', listObj.title); list.id = listObj.id; list.label.id = listObj.label.id; - list.getIssues() - .catch(() => { - // TODO: handle request error - }); + list.getIssues().catch(() => { + // TODO: handle request error + }); }); }) .catch(() => { @@ -57,7 +57,6 @@ export default { clearBlankState: boardsStore.removeBlankState.bind(boardsStore), }, }; - </script> <template> diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue index 843498f0d06..2f31316aa76 100644 --- a/app/assets/javascripts/boards/components/board_card.vue +++ b/app/assets/javascripts/boards/components/board_card.vue @@ -1,77 +1,77 @@ <script> - /* eslint-disable vue/require-default-prop */ - import IssueCardInner from './issue_card_inner.vue'; - import eventHub from '../eventhub'; - import boardsStore from '../stores/boards_store'; +/* eslint-disable vue/require-default-prop */ +import IssueCardInner from './issue_card_inner.vue'; +import eventHub from '../eventhub'; +import boardsStore from '../stores/boards_store'; - export default { - name: 'BoardsIssueCard', - components: { - IssueCardInner, +export default { + name: 'BoardsIssueCard', + components: { + IssueCardInner, + }, + props: { + list: { + type: Object, + default: () => ({}), }, - props: { - list: { - type: Object, - default: () => ({}), - }, - issue: { - type: Object, - default: () => ({}), - }, - issueLinkBase: { - type: String, - default: '', - }, - disabled: { - type: Boolean, - default: false, - }, - index: { - type: Number, - default: 0, - }, - rootPath: { - type: String, - default: '', - }, - groupId: { - type: Number, - }, + issue: { + type: Object, + default: () => ({}), }, - data() { - return { - showDetail: false, - detailIssue: boardsStore.detail, - }; + issueLinkBase: { + type: String, + default: '', }, - computed: { - issueDetailVisible() { - return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id; - }, + disabled: { + type: Boolean, + default: false, }, - methods: { - mouseDown() { - this.showDetail = true; - }, - mouseMove() { - this.showDetail = false; - }, - showIssue(e) { - if (e.target.classList.contains('js-no-trigger')) return; + index: { + type: Number, + default: 0, + }, + rootPath: { + type: String, + default: '', + }, + groupId: { + type: Number, + }, + }, + data() { + return { + showDetail: false, + detailIssue: boardsStore.detail, + }; + }, + computed: { + issueDetailVisible() { + return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id; + }, + }, + methods: { + mouseDown() { + this.showDetail = true; + }, + mouseMove() { + this.showDetail = false; + }, + showIssue(e) { + if (e.target.classList.contains('js-no-trigger')) return; - if (this.showDetail) { - this.showDetail = false; + if (this.showDetail) { + this.showDetail = false; - if (boardsStore.detail.issue && boardsStore.detail.issue.id === this.issue.id) { - eventHub.$emit('clearDetailIssue'); - } else { - eventHub.$emit('newDetailIssue', this.issue); - boardsStore.detail.list = this.list; - } + if (boardsStore.detail.issue && boardsStore.detail.issue.id === this.issue.id) { + eventHub.$emit('clearDetailIssue'); + } else { + eventHub.$emit('newDetailIssue', this.issue); + boardsStore.detail.list = this.list; } - }, + } }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue index 030288a1c9d..ee3dc38bca6 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.vue +++ b/app/assets/javascripts/boards/components/board_new_issue.vue @@ -1,6 +1,6 @@ <script> import $ from 'jquery'; -import { Button } from '@gitlab-org/gitlab-ui'; +import { GlButton } from '@gitlab-org/gitlab-ui'; import eventHub from '../eventhub'; import ProjectSelect from './project_select.vue'; import ListIssue from '../models/issue'; @@ -10,7 +10,7 @@ export default { name: 'BoardNewIssue', components: { ProjectSelect, - 'gl-button': Button, + GlButton, }, props: { groupId: { @@ -62,7 +62,8 @@ export default { eventHub.$emit(`scroll-board-list-${this.list.id}`); this.cancel(); - return this.list.newIssue(issue) + return this.list + .newIssue(issue) .then(() => { // Need this because our jQuery very kindly disables buttons on ALL form submissions $(this.$refs.submitButton).enable(); diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js index 62666954de0..e637e1f1223 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js +++ b/app/assets/javascripts/boards/components/board_sidebar.js @@ -38,7 +38,7 @@ export default Vue.extend({ }; }, computed: { - showSidebar () { + showSidebar() { return Object.keys(this.issue).length; }, milestoneTitle() { @@ -51,18 +51,20 @@ export default Vue.extend({ return this.issue.labels && this.issue.labels.length; }, labelDropdownTitle() { - return this.hasLabels ? sprintf(__('%{firstLabel} +%{labelCount} more'), { - firstLabel: this.issue.labels[0].title, - labelCount: this.issue.labels.length - 1 - }) : __('Label'); + return this.hasLabels + ? sprintf(__('%{firstLabel} +%{labelCount} more'), { + firstLabel: this.issue.labels[0].title, + labelCount: this.issue.labels.length - 1, + }) + : __('Label'); }, selectedLabels() { return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : ''; - } + }, }, watch: { detail: { - handler () { + handler() { if (this.issue.id !== this.detail.issue.id) { $('.block.assignee') .find('input:not(.js-vue)[name="issue[assignee_ids][]"]') @@ -71,17 +73,19 @@ export default Vue.extend({ }); $('.js-issue-board-sidebar', this.$el).each((i, el) => { - $(el).data('glDropdown').clearMenu(); + $(el) + .data('glDropdown') + .clearMenu(); }); } this.issue = this.detail.issue; this.list = this.detail.list; }, - deep: true + deep: true, }, }, - created () { + created() { // Get events from glDropdown eventHub.$on('sidebar.removeAssignee', this.removeAssignee); eventHub.$on('sidebar.addAssignee', this.addAssignee); @@ -94,7 +98,7 @@ export default Vue.extend({ eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees); eventHub.$off('sidebar.saveAssignees', this.saveAssignees); }, - mounted () { + mounted() { new IssuableContext(this.currentUser); new MilestoneSelect(); new DueDateSelectors(); @@ -102,29 +106,30 @@ export default Vue.extend({ new Sidebar(); }, methods: { - closeSidebar () { + closeSidebar() { this.detail.issue = {}; }, - assignSelf () { + assignSelf() { // Notify gl dropdown that we are now assigning to current user this.$refs.assigneeBlock.dispatchEvent(new Event('assignYourself')); this.addAssignee(this.currentUser); this.saveAssignees(); }, - removeAssignee (a) { + removeAssignee(a) { boardsStore.detail.issue.removeAssignee(a); }, - addAssignee (a) { + addAssignee(a) { boardsStore.detail.issue.addAssignee(a); }, - removeAllAssignees () { + removeAllAssignees() { boardsStore.detail.issue.removeAllAssignees(); }, - saveAssignees () { + saveAssignees() { this.loadingAssignees = true; - boardsStore.detail.issue.update() + boardsStore.detail.issue + .update() .then(() => { this.loadingAssignees = false; }) diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue index aa98f35786e..d956777a86b 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.vue +++ b/app/assets/javascripts/boards/components/issue_card_inner.vue @@ -1,142 +1,142 @@ <script> - import $ from 'jquery'; - import Icon from '~/vue_shared/components/icon.vue'; - import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; - import eventHub from '../eventhub'; - import tooltip from '../../vue_shared/directives/tooltip'; - import boardsStore from '../stores/boards_store'; +import $ from 'jquery'; +import Icon from '~/vue_shared/components/icon.vue'; +import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; +import eventHub from '../eventhub'; +import tooltip from '../../vue_shared/directives/tooltip'; +import boardsStore from '../stores/boards_store'; - export default { - components: { - UserAvatarLink, - Icon, - }, - directives: { - tooltip, - }, - props: { - issue: { - type: Object, - required: true, - }, - issueLinkBase: { - type: String, - required: true, - }, - list: { - type: Object, - required: false, - default: () => ({}), - }, - rootPath: { - type: String, - required: true, - }, - updateFilters: { - type: Boolean, - required: false, - default: false, - }, - groupId: { - type: Number, - required: false, - default: null, - }, - }, - data() { - return { - limitBeforeCounter: 3, - maxRender: 4, - maxCounter: 99, - }; +export default { + components: { + UserAvatarLink, + Icon, + }, + directives: { + tooltip, + }, + props: { + issue: { + type: Object, + required: true, }, - computed: { - numberOverLimit() { - return this.issue.assignees.length - this.limitBeforeCounter; - }, - assigneeCounterTooltip() { - return `${this.assigneeCounterLabel} more`; - }, - assigneeCounterLabel() { - if (this.numberOverLimit > this.maxCounter) { - return `${this.maxCounter}+`; - } - - return `+${this.numberOverLimit}`; - }, - shouldRenderCounter() { - if (this.issue.assignees.length <= this.maxRender) { - return false; - } + issueLinkBase: { + type: String, + required: true, + }, + list: { + type: Object, + required: false, + default: () => ({}), + }, + rootPath: { + type: String, + required: true, + }, + updateFilters: { + type: Boolean, + required: false, + default: false, + }, + groupId: { + type: Number, + required: false, + default: null, + }, + }, + data() { + return { + limitBeforeCounter: 3, + maxRender: 4, + maxCounter: 99, + }; + }, + computed: { + numberOverLimit() { + return this.issue.assignees.length - this.limitBeforeCounter; + }, + assigneeCounterTooltip() { + return `${this.assigneeCounterLabel} more`; + }, + assigneeCounterLabel() { + if (this.numberOverLimit > this.maxCounter) { + return `${this.maxCounter}+`; + } - return this.issue.assignees.length > this.numberOverLimit; - }, - issueId() { - if (this.issue.iid) { - return `#${this.issue.iid}`; - } + return `+${this.numberOverLimit}`; + }, + shouldRenderCounter() { + if (this.issue.assignees.length <= this.maxRender) { return false; - }, - showLabelFooter() { - return this.issue.labels.find(l => this.showLabel(l)) !== undefined; - }, - }, - methods: { - isIndexLessThanlimit(index) { - return index < this.limitBeforeCounter; - }, - shouldRenderAssignee(index) { - // Eg. maxRender is 4, - // Render up to all 4 assignees if there are only 4 assigness - // Otherwise render up to the limitBeforeCounter - if (this.issue.assignees.length <= this.maxRender) { - return index < this.maxRender; - } + } - return index < this.limitBeforeCounter; - }, - assigneeUrl(assignee) { - return `${this.rootPath}${assignee.username}`; - }, - assigneeUrlTitle(assignee) { - return `Assigned to ${assignee.name}`; - }, - avatarUrlTitle(assignee) { - return `Avatar for ${assignee.name}`; - }, - showLabel(label) { - if (!label.id) return false; - return true; - }, - filterByLabel(label, e) { - if (!this.updateFilters) return; + return this.issue.assignees.length > this.numberOverLimit; + }, + issueId() { + if (this.issue.iid) { + return `#${this.issue.iid}`; + } + return false; + }, + showLabelFooter() { + return this.issue.labels.find(l => this.showLabel(l)) !== undefined; + }, + }, + methods: { + isIndexLessThanlimit(index) { + return index < this.limitBeforeCounter; + }, + shouldRenderAssignee(index) { + // Eg. maxRender is 4, + // Render up to all 4 assignees if there are only 4 assigness + // Otherwise render up to the limitBeforeCounter + if (this.issue.assignees.length <= this.maxRender) { + return index < this.maxRender; + } + + return index < this.limitBeforeCounter; + }, + assigneeUrl(assignee) { + return `${this.rootPath}${assignee.username}`; + }, + assigneeUrlTitle(assignee) { + return `Assigned to ${assignee.name}`; + }, + avatarUrlTitle(assignee) { + return `Avatar for ${assignee.name}`; + }, + showLabel(label) { + if (!label.id) return false; + return true; + }, + filterByLabel(label, e) { + if (!this.updateFilters) return; - const filterPath = boardsStore.filter.path.split('&'); - const labelTitle = encodeURIComponent(label.title); - const param = `label_name[]=${labelTitle}`; - const labelIndex = filterPath.indexOf(param); - $(e.currentTarget).tooltip('hide'); + const filterPath = boardsStore.filter.path.split('&'); + const labelTitle = encodeURIComponent(label.title); + const param = `label_name[]=${labelTitle}`; + const labelIndex = filterPath.indexOf(param); + $(e.currentTarget).tooltip('hide'); - if (labelIndex === -1) { - filterPath.push(param); - } else { - filterPath.splice(labelIndex, 1); - } + if (labelIndex === -1) { + filterPath.push(param); + } else { + filterPath.splice(labelIndex, 1); + } - boardsStore.filter.path = filterPath.join('&'); + boardsStore.filter.path = filterPath.join('&'); - boardsStore.updateFiltersUrl(); + boardsStore.updateFiltersUrl(); - eventHub.$emit('updateTokens'); - }, - labelStyle(label) { - return { - backgroundColor: label.color, - color: label.textColor, - }; - }, - }, - }; + eventHub.$emit('updateTokens'); + }, + labelStyle(label) { + return { + backgroundColor: label.color, + color: label.textColor, + }; + }, + }, +}; </script> <template> <div> diff --git a/app/assets/javascripts/boards/components/modal/empty_state.vue b/app/assets/javascripts/boards/components/modal/empty_state.vue index dbd69f84526..795ba864545 100644 --- a/app/assets/javascripts/boards/components/modal/empty_state.vue +++ b/app/assets/javascripts/boards/components/modal/empty_state.vue @@ -20,7 +20,7 @@ export default { computed: { contents() { const obj = { - title: 'You haven\'t added any issues to your project yet', + title: "You haven't added any issues to your project yet", content: ` An issue can be a bug, a todo or a feature request that needs to be discussed in a project. Besides, issues are searchable and filterable. @@ -28,7 +28,7 @@ export default { }; if (this.activeTab === 'selected') { - obj.title = 'You haven\'t selected any issues yet'; + obj.title = "You haven't selected any issues yet"; obj.content = ` Go back to <strong>Open issues</strong> and select some issues to add to your board. diff --git a/app/assets/javascripts/boards/components/modal/footer.vue b/app/assets/javascripts/boards/components/modal/footer.vue index 268ca6bca13..d51597ed22d 100644 --- a/app/assets/javascripts/boards/components/modal/footer.vue +++ b/app/assets/javascripts/boards/components/modal/footer.vue @@ -42,19 +42,17 @@ export default { const req = this.buildUpdateRequest(list); // Post the data to the backend - gl.boardService - .bulkUpdate(issueIds, req) - .catch(() => { - Flash(__('Failed to update issues, please try again.')); + gl.boardService.bulkUpdate(issueIds, req).catch(() => { + Flash(__('Failed to update issues, please try again.')); - selectedIssues.forEach((issue) => { - list.removeIssue(issue); - list.issuesSize -= 1; - }); + selectedIssues.forEach(issue => { + list.removeIssue(issue); + list.issuesSize -= 1; }); + }); // Add the issues on the frontend - selectedIssues.forEach((issue) => { + selectedIssues.forEach(issue => { list.addIssue(issue); list.issuesSize += 1; }); diff --git a/app/assets/javascripts/boards/components/modal/header.vue b/app/assets/javascripts/boards/components/modal/header.vue index 979fb4d7199..fc6cefa89a9 100644 --- a/app/assets/javascripts/boards/components/modal/header.vue +++ b/app/assets/javascripts/boards/components/modal/header.vue @@ -1,52 +1,52 @@ <script> - import ModalFilters from './filters'; - import ModalTabs from './tabs.vue'; - import ModalStore from '../../stores/modal_store'; - import modalMixin from '../../mixins/modal_mixins'; +import ModalFilters from './filters'; +import ModalTabs from './tabs.vue'; +import ModalStore from '../../stores/modal_store'; +import modalMixin from '../../mixins/modal_mixins'; - export default { - components: { - ModalTabs, - ModalFilters, +export default { + components: { + ModalTabs, + ModalFilters, + }, + mixins: [modalMixin], + props: { + projectId: { + type: Number, + required: true, }, - mixins: [modalMixin], - props: { - projectId: { - type: Number, - required: true, - }, - milestonePath: { - type: String, - required: true, - }, - labelPath: { - type: String, - required: true, - }, + milestonePath: { + type: String, + required: true, }, - data() { - return ModalStore.store; + labelPath: { + type: String, + required: true, }, - computed: { - selectAllText() { - if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) { - return 'Select all'; - } + }, + data() { + return ModalStore.store; + }, + computed: { + selectAllText() { + if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) { + return 'Select all'; + } - return 'Deselect all'; - }, - showSearch() { - return this.activeTab === 'all' && !this.loading && this.issuesCount > 0; - }, + return 'Deselect all'; }, - methods: { - toggleAll() { - this.$refs.selectAllBtn.blur(); + showSearch() { + return this.activeTab === 'all' && !this.loading && this.issuesCount > 0; + }, + }, + methods: { + toggleAll() { + this.$refs.selectAllBtn.blur(); - ModalStore.toggleAll(); - }, + ModalStore.toggleAll(); }, - }; + }, +}; </script> <template> <div> diff --git a/app/assets/javascripts/boards/components/modal/index.vue b/app/assets/javascripts/boards/components/modal/index.vue index 0c4c709324d..40949cc0656 100644 --- a/app/assets/javascripts/boards/components/modal/index.vue +++ b/app/assets/javascripts/boards/components/modal/index.vue @@ -1,143 +1,144 @@ <script> - /* global ListIssue */ - import { urlParamsToObject } from '~/lib/utils/common_utils'; - import ModalHeader from './header.vue'; - import ModalList from './list.vue'; - import ModalFooter from './footer.vue'; - import EmptyState from './empty_state.vue'; - import ModalStore from '../../stores/modal_store'; +/* global ListIssue */ +import { urlParamsToObject } from '~/lib/utils/common_utils'; +import ModalHeader from './header.vue'; +import ModalList from './list.vue'; +import ModalFooter from './footer.vue'; +import EmptyState from './empty_state.vue'; +import ModalStore from '../../stores/modal_store'; - export default { - components: { - EmptyState, - ModalHeader, - ModalList, - ModalFooter, +export default { + components: { + EmptyState, + ModalHeader, + ModalList, + ModalFooter, + }, + props: { + newIssuePath: { + type: String, + required: true, }, - props: { - newIssuePath: { - type: String, - required: true, - }, - emptyStateSvg: { - type: String, - required: true, - }, - issueLinkBase: { - type: String, - required: true, - }, - rootPath: { - type: String, - required: true, - }, - projectId: { - type: Number, - required: true, - }, - milestonePath: { - type: String, - required: true, - }, - labelPath: { - type: String, - required: true, - }, + emptyStateSvg: { + type: String, + required: true, }, - data() { - return ModalStore.store; + issueLinkBase: { + type: String, + required: true, }, - computed: { - showList() { - if (this.activeTab === 'selected') { - return this.selectedIssues.length > 0; - } + rootPath: { + type: String, + required: true, + }, + projectId: { + type: Number, + required: true, + }, + milestonePath: { + type: String, + required: true, + }, + labelPath: { + type: String, + required: true, + }, + }, + data() { + return ModalStore.store; + }, + computed: { + showList() { + if (this.activeTab === 'selected') { + return this.selectedIssues.length > 0; + } - return this.issuesCount > 0; - }, - showEmptyState() { - if (!this.loading && this.issuesCount === 0) { - return true; - } + return this.issuesCount > 0; + }, + showEmptyState() { + if (!this.loading && this.issuesCount === 0) { + return true; + } - return this.activeTab === 'selected' && this.selectedIssues.length === 0; - }, + return this.activeTab === 'selected' && this.selectedIssues.length === 0; }, - watch: { - page() { - this.loadIssues(); - }, - showAddIssuesModal() { - if (this.showAddIssuesModal && !this.issues.length) { - this.loading = true; + }, + watch: { + page() { + this.loadIssues(); + }, + showAddIssuesModal() { + if (this.showAddIssuesModal && !this.issues.length) { + this.loading = true; + const loadingDone = () => { + this.loading = false; + }; + + this.loadIssues() + .then(loadingDone) + .catch(loadingDone); + } else if (!this.showAddIssuesModal) { + this.issues = []; + this.selectedIssues = []; + this.issuesCount = false; + } + }, + filter: { + handler() { + if (this.$el.tagName) { + this.page = 1; + this.filterLoading = true; const loadingDone = () => { - this.loading = false; + this.filterLoading = false; }; - this.loadIssues() + this.loadIssues(true) .then(loadingDone) .catch(loadingDone); - } else if (!this.showAddIssuesModal) { - this.issues = []; - this.selectedIssues = []; - this.issuesCount = false; } }, - filter: { - handler() { - if (this.$el.tagName) { - this.page = 1; - this.filterLoading = true; - const loadingDone = () => { - this.filterLoading = false; - }; - - this.loadIssues(true) - .then(loadingDone) - .catch(loadingDone); - } - }, - deep: true, - }, + deep: true, }, - created() { - this.page = 1; - }, - methods: { - loadIssues(clearIssues = false) { - if (!this.showAddIssuesModal) return false; + }, + created() { + this.page = 1; + }, + methods: { + loadIssues(clearIssues = false) { + if (!this.showAddIssuesModal) return false; - return gl.boardService.getBacklog({ + return gl.boardService + .getBacklog({ ...urlParamsToObject(this.filter.path), page: this.page, per: this.perPage, }) - .then(res => res.data) - .then(data => { - if (clearIssues) { - this.issues = []; - } + .then(res => res.data) + .then(data => { + if (clearIssues) { + this.issues = []; + } - data.issues.forEach(issueObj => { - const issue = new ListIssue(issueObj); - const foundSelectedIssue = ModalStore.findSelectedIssue(issue); - issue.selected = !!foundSelectedIssue; + data.issues.forEach(issueObj => { + const issue = new ListIssue(issueObj); + const foundSelectedIssue = ModalStore.findSelectedIssue(issue); + issue.selected = !!foundSelectedIssue; - this.issues.push(issue); - }); + this.issues.push(issue); + }); - this.loadingNewPage = false; + this.loadingNewPage = false; - if (!this.issuesCount) { - this.issuesCount = data.size; - } - }) - .catch(() => { - // TODO: handle request error - }); - }, + if (!this.issuesCount) { + this.issuesCount = data.size; + } + }) + .catch(() => { + // TODO: handle request error + }); }, - }; + }, +}; </script> <template> <div diff --git a/app/assets/javascripts/boards/components/modal/list.vue b/app/assets/javascripts/boards/components/modal/list.vue index c93fd9f415c..e11f398e70d 100644 --- a/app/assets/javascripts/boards/components/modal/list.vue +++ b/app/assets/javascripts/boards/components/modal/list.vue @@ -1,120 +1,120 @@ <script> - import Icon from '~/vue_shared/components/icon.vue'; - import bp from '../../../breakpoints'; - import ModalStore from '../../stores/modal_store'; - import IssueCardInner from '../issue_card_inner.vue'; +import Icon from '~/vue_shared/components/icon.vue'; +import bp from '../../../breakpoints'; +import ModalStore from '../../stores/modal_store'; +import IssueCardInner from '../issue_card_inner.vue'; - export default { - components: { - IssueCardInner, - Icon, +export default { + components: { + IssueCardInner, + Icon, + }, + props: { + issueLinkBase: { + type: String, + required: true, }, - props: { - issueLinkBase: { - type: String, - required: true, - }, - rootPath: { - type: String, - required: true, - }, - emptyStateSvg: { - type: String, - required: true, - }, + rootPath: { + type: String, + required: true, }, - data() { - return ModalStore.store; + emptyStateSvg: { + type: String, + required: true, }, - computed: { - loopIssues() { - if (this.activeTab === 'all') { - return this.issues; - } + }, + data() { + return ModalStore.store; + }, + computed: { + loopIssues() { + if (this.activeTab === 'all') { + return this.issues; + } - return this.selectedIssues; - }, - groupedIssues() { - const groups = []; - this.loopIssues.forEach((issue, i) => { - const index = i % this.columns; + return this.selectedIssues; + }, + groupedIssues() { + const groups = []; + this.loopIssues.forEach((issue, i) => { + const index = i % this.columns; - if (!groups[index]) { - groups.push([]); - } + if (!groups[index]) { + groups.push([]); + } - groups[index].push(issue); - }); + groups[index].push(issue); + }); - return groups; - }, + return groups; }, - watch: { - activeTab() { - if (this.activeTab === 'all') { - ModalStore.purgeUnselectedIssues(); - } - }, + }, + watch: { + activeTab() { + if (this.activeTab === 'all') { + ModalStore.purgeUnselectedIssues(); + } }, - mounted() { - this.scrollHandlerWrapper = this.scrollHandler.bind(this); - this.setColumnCountWrapper = this.setColumnCount.bind(this); - this.setColumnCount(); + }, + mounted() { + this.scrollHandlerWrapper = this.scrollHandler.bind(this); + this.setColumnCountWrapper = this.setColumnCount.bind(this); + this.setColumnCount(); - this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper); - window.addEventListener('resize', this.setColumnCountWrapper); + this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper); + window.addEventListener('resize', this.setColumnCountWrapper); + }, + beforeDestroy() { + this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper); + window.removeEventListener('resize', this.setColumnCountWrapper); + }, + methods: { + scrollHandler() { + const currentPage = Math.floor(this.issues.length / this.perPage); + + if ( + this.scrollTop() > this.scrollHeight() - 100 && + !this.loadingNewPage && + currentPage === this.page + ) { + this.loadingNewPage = true; + this.page += 1; + } }, - beforeDestroy() { - this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper); - window.removeEventListener('resize', this.setColumnCountWrapper); + toggleIssue(e, issue) { + if (e.target.tagName !== 'A') { + ModalStore.toggleIssue(issue); + } }, - methods: { - scrollHandler() { - const currentPage = Math.floor(this.issues.length / this.perPage); - - if ( - this.scrollTop() > this.scrollHeight() - 100 && - !this.loadingNewPage && - currentPage === this.page - ) { - this.loadingNewPage = true; - this.page += 1; - } - }, - toggleIssue(e, issue) { - if (e.target.tagName !== 'A') { - ModalStore.toggleIssue(issue); - } - }, - listHeight() { - return this.$refs.list.getBoundingClientRect().height; - }, - scrollHeight() { - return this.$refs.list.scrollHeight; - }, - scrollTop() { - return this.$refs.list.scrollTop + this.listHeight(); - }, - showIssue(issue) { - if (this.activeTab === 'all') return true; + listHeight() { + return this.$refs.list.getBoundingClientRect().height; + }, + scrollHeight() { + return this.$refs.list.scrollHeight; + }, + scrollTop() { + return this.$refs.list.scrollTop + this.listHeight(); + }, + showIssue(issue) { + if (this.activeTab === 'all') return true; - const index = ModalStore.selectedIssueIndex(issue); + const index = ModalStore.selectedIssueIndex(issue); - return index !== -1; - }, - setColumnCount() { - const breakpoint = bp.getBreakpointSize(); + return index !== -1; + }, + setColumnCount() { + const breakpoint = bp.getBreakpointSize(); - if (breakpoint === 'lg' || breakpoint === 'md') { - this.columns = 3; - } else if (breakpoint === 'sm') { - this.columns = 2; - } else { - this.columns = 1; - } - }, + if (breakpoint === 'lg' || breakpoint === 'md') { + this.columns = 3; + } else if (breakpoint === 'sm') { + this.columns = 2; + } else { + this.columns = 1; + } }, - }; + }, +}; </script> <template> <section diff --git a/app/assets/javascripts/boards/components/modal/lists_dropdown.vue b/app/assets/javascripts/boards/components/modal/lists_dropdown.vue index 3baac08d411..20665f903d5 100644 --- a/app/assets/javascripts/boards/components/modal/lists_dropdown.vue +++ b/app/assets/javascripts/boards/components/modal/lists_dropdown.vue @@ -1,12 +1,12 @@ <script> -import { Link } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab-org/gitlab-ui'; import Icon from '~/vue_shared/components/icon.vue'; import ModalStore from '../../stores/modal_store'; import boardsStore from '../../stores/boards_store'; export default { components: { - 'gl-link': Link, + GlLink, Icon, }, data() { diff --git a/app/assets/javascripts/boards/components/modal/tabs.vue b/app/assets/javascripts/boards/components/modal/tabs.vue index d926b080094..5d661590e8e 100644 --- a/app/assets/javascripts/boards/components/modal/tabs.vue +++ b/app/assets/javascripts/boards/components/modal/tabs.vue @@ -1,21 +1,21 @@ <script> - import ModalStore from '../../stores/modal_store'; - import modalMixin from '../../mixins/modal_mixins'; +import ModalStore from '../../stores/modal_store'; +import modalMixin from '../../mixins/modal_mixins'; - export default { - mixins: [modalMixin], - data() { - return ModalStore.store; +export default { + mixins: [modalMixin], + data() { + return ModalStore.store; + }, + computed: { + selectedCount() { + return ModalStore.selectedCount(); }, - computed: { - selectedCount() { - return ModalStore.selectedCount(); - }, - }, - destroyed() { - this.activeTab = 'all'; - }, - }; + }, + destroyed() { + this.activeTab = 'all'; + }, +}; </script> <template> <div class="top-area prepend-top-10 append-bottom-10"> diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js index 2c2045f8901..f7016561f93 100644 --- a/app/assets/javascripts/boards/components/new_list_dropdown.js +++ b/app/assets/javascripts/boards/components/new_list_dropdown.js @@ -6,36 +6,41 @@ import _ from 'underscore'; import CreateLabelDropdown from '../../create_label'; import boardsStore from '../stores/boards_store'; -$(document).off('created.label').on('created.label', (e, label) => { - boardsStore.new({ - title: label.title, - position: boardsStore.state.lists.length - 2, - list_type: 'label', - label: { - id: label.id, +$(document) + .off('created.label') + .on('created.label', (e, label) => { + boardsStore.new({ title: label.title, - color: label.color, - }, + position: boardsStore.state.lists.length - 2, + list_type: 'label', + label: { + id: label.id, + title: label.title, + color: label.color, + }, + }); }); -}); export default function initNewListDropdown() { - $('.js-new-board-list').each(function () { + $('.js-new-board-list').each(function() { const $this = $(this); - new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespacePath'), $this.data('projectPath')); + new CreateLabelDropdown( + $this.closest('.dropdown').find('.dropdown-new-label'), + $this.data('namespacePath'), + $this.data('projectPath'), + ); $this.glDropdown({ data(term, callback) { - axios.get($this.attr('data-list-labels-path')) - .then(({ data }) => { - callback(data); - }); + axios.get($this.attr('data-list-labels-path')).then(({ data }) => { + callback(data); + }); }, - renderRow (label) { + renderRow(label) { const active = boardsStore.findList('title', label.title); const $li = $('<li />'); const $a = $('<a />', { - class: (active ? `is-active js-board-list-${active.id}` : ''), + class: active ? `is-active js-board-list-${active.id}` : '', text: label.title, href: '#', }); @@ -53,7 +58,7 @@ export default function initNewListDropdown() { selectable: true, multiSelect: true, containerSelector: '.js-tab-container-labels .dropdown-page-one .dropdown-content', - clicked (options) { + clicked(options) { const { e } = options; const label = options.selectedObj; e.preventDefault(); diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue index 4e8fe16160a..0f01a2a6c09 100644 --- a/app/assets/javascripts/boards/components/project_select.vue +++ b/app/assets/javascripts/boards/components/project_select.vue @@ -46,7 +46,7 @@ export default { selectable: true, data: (term, callback) => { this.loading = true; - return Api.groupProjects(this.groupId, term, {}, projects => { + return Api.groupProjects(this.groupId, term, { with_issues_enabled: true }, projects => { this.loading = false; callback(projects); }); @@ -54,7 +54,9 @@ export default { renderRow(project) { return ` <li> - <a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}"> + <a href='#' class='dropdown-menu-link' data-project-id="${ + project.id + }" data-project-name="${project.name}"> ${_.escape(project.name)} </a> </li> diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue index b8f2e324d43..d681e6a431c 100644 --- a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue +++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue @@ -1,79 +1,77 @@ <script> - import Vue from 'vue'; - import Flash from '../../../flash'; - import { __ } from '../../../locale'; - import boardsStore from '../../stores/boards_store'; +import Vue from 'vue'; +import Flash from '../../../flash'; +import { __ } from '../../../locale'; +import boardsStore from '../../stores/boards_store'; - export default Vue.extend({ - props: { - issue: { - type: Object, - required: true, - }, - list: { - type: Object, - required: true, - }, +export default Vue.extend({ + props: { + issue: { + type: Object, + required: true, }, - computed: { - updateUrl() { - return this.issue.path; - }, + list: { + type: Object, + required: true, }, - methods: { - removeIssue() { - const { issue } = this; - const lists = issue.getLists(); - const req = this.buildPatchRequest(issue, lists); - - const data = { - issue: this.seedPatchRequest(issue, req), - }; + }, + computed: { + updateUrl() { + return this.issue.path; + }, + }, + methods: { + removeIssue() { + const { issue } = this; + const lists = issue.getLists(); + const req = this.buildPatchRequest(issue, lists); - if (data.issue.label_ids.length === 0) { - data.issue.label_ids = ['']; - } + const data = { + issue: this.seedPatchRequest(issue, req), + }; - // Post the remove data - Vue.http.patch(this.updateUrl, data).catch(() => { - Flash(__('Failed to remove issue from board, please try again.')); + if (data.issue.label_ids.length === 0) { + data.issue.label_ids = ['']; + } - lists.forEach(list => { - list.addIssue(issue); - }); - }); + // Post the remove data + Vue.http.patch(this.updateUrl, data).catch(() => { + Flash(__('Failed to remove issue from board, please try again.')); - // Remove from the frontend store lists.forEach(list => { - list.removeIssue(issue); + list.addIssue(issue); }); + }); - boardsStore.detail.issue = {}; - }, - /** - * Build the default patch request. - */ - buildPatchRequest(issue, lists) { - const listLabelIds = lists.map(list => list.label.id); + // Remove from the frontend store + lists.forEach(list => { + list.removeIssue(issue); + }); - const labelIds = issue.labels - .map(label => label.id) - .filter(id => !listLabelIds.includes(id)); + boardsStore.detail.issue = {}; + }, + /** + * Build the default patch request. + */ + buildPatchRequest(issue, lists) { + const listLabelIds = lists.map(list => list.label.id); + + const labelIds = issue.labels.map(label => label.id).filter(id => !listLabelIds.includes(id)); - return { - label_ids: labelIds, - }; - }, - /** - * Seed the given patch request. - * - * (This is overridden in EE) - */ - seedPatchRequest(issue, req) { - return req; - }, + return { + label_ids: labelIds, + }; + }, + /** + * Seed the given patch request. + * + * (This is overridden in EE) + */ + seedPatchRequest(issue, req) { + return req; }, - }); + }, +}); </script> <template> <div diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index acf41e5689e..c14d69c5d18 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -32,7 +32,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager { const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token'); // Remove all the tokens as they will be replaced by the search manager - [].forEach.call(tokens, (el) => { + [].forEach.call(tokens, el => { el.parentNode.removeChild(el); }); @@ -50,7 +50,10 @@ export default class FilteredSearchBoards extends FilteredSearchManager { canEdit(tokenName, tokenValue) { if (this.cantEdit.includes(tokenName)) return false; - return this.cantEditWithValue.findIndex(token => token.name === tokenName && - token.value === tokenValue) === -1; + return ( + this.cantEditWithValue.findIndex( + token => token.name === tokenName && token.value === tokenValue, + ) === -1 + ); } } diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 91861f2f9ee..61a3072ac27 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -32,9 +32,9 @@ export default () => { const $boardApp = document.getElementById('board-app'); // check for browser back and trigger a hard reload to circumvent browser caching. - window.addEventListener('pageshow', (event) => { - const isNavTypeBackForward = window.performance && - window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD; + window.addEventListener('pageshow', event => { + const isNavTypeBackForward = + window.performance && window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD; if (event.persisted || isNavTypeBackForward) { window.location.reload(); diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js b/app/assets/javascripts/boards/mixins/sortable_default_options.js index c9cde4effb9..983b28d2e67 100644 --- a/app/assets/javascripts/boards/mixins/sortable_default_options.js +++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js @@ -4,7 +4,8 @@ import $ from 'jquery'; import sortableConfig from '../../sortable/sortable_config'; export function sortableStart() { - $('.has-tooltip').tooltip('hide') + $('.has-tooltip') + .tooltip('hide') .tooltip('disable'); document.body.classList.add('is-dragging'); } @@ -15,7 +16,8 @@ export function sortableEnd() { } export function getBoardSortableDefaultOptions(obj) { - const touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch; + const touchEnabled = + 'ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch); const defaultSortOptions = Object.assign({}, sortableConfig, { filter: '.board-delete, .btn', @@ -26,6 +28,8 @@ export function getBoardSortableDefaultOptions(obj) { onEnd: sortableEnd, }); - Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; }); + Object.keys(obj).forEach(key => { + defaultSortOptions[key] = obj[key]; + }); return defaultSortOptions; } diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js index 52d04389b88..bb3b2865934 100644 --- a/app/assets/javascripts/boards/models/issue.js +++ b/app/assets/javascripts/boards/models/issue.js @@ -9,7 +9,7 @@ import IssueProject from './project'; import boardsStore from '../stores/boards_store'; class ListIssue { - constructor (obj, defaultAvatar) { + constructor(obj, defaultAvatar) { this.id = obj.id; this.iid = obj.iid; this.title = obj.title; @@ -39,54 +39,54 @@ class ListIssue { this.milestone = new ListMilestone(obj.milestone); } - obj.labels.forEach((label) => { + obj.labels.forEach(label => { this.labels.push(new ListLabel(label)); }); this.assignees = obj.assignees.map(a => new ListAssignee(a, defaultAvatar)); } - addLabel (label) { + addLabel(label) { if (!this.findLabel(label)) { this.labels.push(new ListLabel(label)); } } - findLabel (findLabel) { + findLabel(findLabel) { return this.labels.filter(label => label.title === findLabel.title)[0]; } - removeLabel (removeLabel) { + removeLabel(removeLabel) { if (removeLabel) { this.labels = this.labels.filter(label => removeLabel.title !== label.title); } } - removeLabels (labels) { + removeLabels(labels) { labels.forEach(this.removeLabel.bind(this)); } - addAssignee (assignee) { + addAssignee(assignee) { if (!this.findAssignee(assignee)) { this.assignees.push(new ListAssignee(assignee)); } } - findAssignee (findAssignee) { + findAssignee(findAssignee) { return this.assignees.filter(assignee => assignee.id === findAssignee.id)[0]; } - removeAssignee (removeAssignee) { + removeAssignee(removeAssignee) { if (removeAssignee) { this.assignees = this.assignees.filter(assignee => assignee.id !== removeAssignee.id); } } - removeAllAssignees () { + removeAllAssignees() { this.assignees = []; } - getLists () { + getLists() { return boardsStore.state.lists.filter(list => list.findIssue(this.id)); } @@ -102,14 +102,14 @@ class ListIssue { this.isLoading[key] = value; } - update () { + update() { const data = { issue: { milestone_id: this.milestone ? this.milestone.id : null, due_date: this.dueDate, - assignee_ids: this.assignees.length > 0 ? this.assignees.map((u) => u.id) : [0], - label_ids: this.labels.map((label) => label.id) - } + assignee_ids: this.assignees.length > 0 ? this.assignees.map(u => u.id) : [0], + label_ids: this.labels.map(label => label.id), + }, }; if (!data.issue.label_ids.length) { diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index 3161f1da8c9..dd3feedbc0e 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -234,11 +234,11 @@ class List { }); } - getTypeInfo (type) { + getTypeInfo(type) { return TYPES[type] || {}; } - onNewIssueResponse (issue, data) { + onNewIssueResponse(issue, data) { issue.id = data.id; issue.iid = data.iid; issue.project = data.project; diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js index 029b0971f2c..3de6eb056c2 100644 --- a/app/assets/javascripts/boards/services/board_service.js +++ b/app/assets/javascripts/boards/services/board_service.js @@ -19,7 +19,9 @@ export default class BoardService { } static generateIssuePath(boardId, id) { - return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${id ? `/${id}` : ''}`; + return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${ + id ? `/${id}` : '' + }`; } all() { @@ -54,7 +56,9 @@ export default class BoardService { getIssuesForList(id, filter = {}) { const data = { id }; - Object.keys(filter).forEach((key) => { data[key] = filter[key]; }); + Object.keys(filter).forEach(key => { + data[key] = filter[key]; + }); return axios.get(mergeUrlParams(data, this.generateIssuesPath(id))); } @@ -75,7 +79,9 @@ export default class BoardService { } getBacklog(data) { - return axios.get(mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`)); + return axios.get( + mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`), + ); } bulkUpdate(issueIds, extraData = {}) { diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index 471955747fd..eefe14a1d79 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -20,20 +20,20 @@ const boardsStore = { issue: {}, list: {}, }, - create () { + create() { this.state.lists = []; this.filter.path = getUrlParamsArray().join('&'); this.detail = { issue: {}, }; }, - addList (listObj, defaultAvatar) { + addList(listObj, defaultAvatar) { const list = new List(listObj, defaultAvatar); this.state.lists.push(list); return list; }, - new (listObj) { + new(listObj) { const list = this.addList(listObj); const backlogList = this.findList('type', 'backlog', 'backlog'); @@ -50,44 +50,44 @@ const boardsStore = { }); this.removeBlankState(); }, - updateNewListDropdown (listId) { + updateNewListDropdown(listId) { $(`.js-board-list-${listId}`).removeClass('is-active'); }, - shouldAddBlankState () { + shouldAddBlankState() { // Decide whether to add the blank state - return !(this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0]); + return !this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0]; }, - addBlankState () { + addBlankState() { if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return; this.addList({ id: 'blank', list_type: 'blank', title: 'Welcome to your Issue Board!', - position: 0 + position: 0, }); this.state.lists = _.sortBy(this.state.lists, 'position'); }, - removeBlankState () { + removeBlankState() { this.removeList('blank'); Cookies.set('issue_board_welcome_hidden', 'true', { expires: 365 * 10, - path: '' + path: '', }); }, - welcomeIsHidden () { + welcomeIsHidden() { return Cookies.get('issue_board_welcome_hidden') === 'true'; }, - removeList (id, type = 'blank') { + removeList(id, type = 'blank') { const list = this.findList('id', id, type); if (!list) return; this.state.lists = this.state.lists.filter(list => list.id !== id); }, - moveList (listFrom, orderLists) { + moveList(listFrom, orderLists) { orderLists.forEach((id, i) => { const list = this.findList('id', parseInt(id, 10)); @@ -95,22 +95,25 @@ const boardsStore = { }); listFrom.update(); }, - moveIssueToList (listFrom, listTo, issue, newIndex) { + moveIssueToList(listFrom, listTo, issue, newIndex) { const issueTo = listTo.findIssue(issue.id); const issueLists = issue.getLists(); const listLabels = issueLists.map(listIssue => listIssue.label); if (!issueTo) { // Check if target list assignee is already present in this issue - if ((listTo.type === 'assignee' && listFrom.type === 'assignee') && - issue.findAssignee(listTo.assignee)) { + if ( + listTo.type === 'assignee' && + listFrom.type === 'assignee' && + issue.findAssignee(listTo.assignee) + ) { const targetIssue = listTo.findIssue(issue.id); targetIssue.removeAssignee(listFrom.assignee); } else if (listTo.type === 'milestone') { const currentMilestone = issue.milestone; const currentLists = this.state.lists - .filter(list => (list.type === 'milestone' && list.id !== listTo.id)) - .filter(list => list.issues.some(listIssue => issue.id === listIssue.id)); + .filter(list => list.type === 'milestone' && list.id !== listTo.id) + .filter(list => list.issues.some(listIssue => issue.id === listIssue.id)); issue.removeMilestone(currentMilestone); issue.addMilestone(listTo.milestone); @@ -126,7 +129,7 @@ const boardsStore = { } if (listTo.type === 'closed' && listFrom.type !== 'backlog') { - issueLists.forEach((list) => { + issueLists.forEach(list => { list.removeIssue(issue); }); issue.removeLabels(listLabels); @@ -144,26 +147,28 @@ const boardsStore = { return ( (listTo.type !== 'label' && listFrom.type === 'assignee') || (listTo.type !== 'assignee' && listFrom.type === 'label') || - (listFrom.type === 'backlog') + listFrom.type === 'backlog' ); }, - moveIssueInList (list, issue, oldIndex, newIndex, idArray) { + moveIssueInList(list, issue, oldIndex, newIndex, idArray) { const beforeId = parseInt(idArray[newIndex - 1], 10) || null; const afterId = parseInt(idArray[newIndex + 1], 10) || null; list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId); }, - findList (key, val, type = 'label') { - const filteredList = this.state.lists.filter((list) => { - const byType = type ? (list.type === type) || (list.type === 'assignee') || (list.type === 'milestone') : true; + findList(key, val, type = 'label') { + const filteredList = this.state.lists.filter(list => { + const byType = type + ? list.type === type || list.type === 'assignee' || list.type === 'milestone' + : true; return list[key] === val && byType; }); return filteredList[0]; }, - updateFiltersUrl () { + updateFiltersUrl() { window.history.pushState(null, null, `?${this.filter.path}`); - } + }, }; // hacks added in order to allow milestone_select to function properly diff --git a/app/assets/javascripts/boards/stores/modal_store.js b/app/assets/javascripts/boards/stores/modal_store.js index 0d9ac367a70..b7228bf7bf5 100644 --- a/app/assets/javascripts/boards/stores/modal_store.js +++ b/app/assets/javascripts/boards/stores/modal_store.js @@ -40,7 +40,7 @@ class ModalStore { toggleAll() { const select = this.selectedCount() !== this.store.issues.length; - this.store.issues.forEach((issue) => { + this.store.issues.forEach(issue => { const issueUpdate = issue; if (issueUpdate.selected !== select) { @@ -69,13 +69,14 @@ class ModalStore { removeSelectedIssue(issue, forcePurge = false) { if (this.store.activeTab === 'all' || forcePurge) { - this.store.selectedIssues = this.store.selectedIssues - .filter(fIssue => fIssue.id !== issue.id); + this.store.selectedIssues = this.store.selectedIssues.filter( + fIssue => fIssue.id !== issue.id, + ); } } purgeUnselectedIssues() { - this.store.selectedIssues.forEach((issue) => { + this.store.selectedIssues.forEach(issue => { if (!issue.selected) { this.removeSelectedIssue(issue, true); } @@ -87,8 +88,7 @@ class ModalStore { } findSelectedIssue(issue) { - return this.store.selectedIssues - .filter(filteredIssue => filteredIssue.id === issue.id)[0]; + return this.store.selectedIssues.filter(filteredIssue => filteredIssue.id === issue.id)[0]; } } diff --git a/app/assets/javascripts/breadcrumb.js b/app/assets/javascripts/breadcrumb.js index 1474d93dde6..a37838694ec 100644 --- a/app/assets/javascripts/breadcrumb.js +++ b/app/assets/javascripts/breadcrumb.js @@ -1,6 +1,6 @@ import $ from 'jquery'; -export const addTooltipToEl = (el) => { +export const addTooltipToEl = el => { const textEl = el.querySelector('.js-breadcrumb-item-text'); if (textEl && textEl.scrollWidth > textEl.offsetWidth) { @@ -14,17 +14,18 @@ export default () => { const breadcrumbs = document.querySelector('.js-breadcrumbs-list'); if (breadcrumbs) { - const topLevelLinks = [...breadcrumbs.children].filter(el => !el.classList.contains('dropdown')) + const topLevelLinks = [...breadcrumbs.children] + .filter(el => !el.classList.contains('dropdown')) .map(el => el.querySelector('a')) .filter(el => el); const $expander = $('.js-breadcrumbs-collapsed-expander'); topLevelLinks.forEach(el => addTooltipToEl(el)); - $expander.closest('.dropdown') - .on('show.bs.dropdown hide.bs.dropdown', (e) => { - $('.js-breadcrumbs-collapsed-expander', e.currentTarget).toggleClass('open') - .tooltip('hide'); - }); + $expander.closest('.dropdown').on('show.bs.dropdown hide.bs.dropdown', e => { + $('.js-breadcrumbs-collapsed-expander', e.currentTarget) + .toggleClass('open') + .tooltip('hide'); + }); } }; diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js index e338376fcaa..97a1645aa51 100644 --- a/app/assets/javascripts/build_artifacts.js +++ b/app/assets/javascripts/build_artifacts.js @@ -12,16 +12,16 @@ export default class BuildArtifacts { } // eslint-disable-next-line class-methods-use-this disablePropagation() { - $('.top-block').on('click', '.download', function (e) { + $('.top-block').on('click', '.download', function(e) { return e.stopPropagation(); }); - return $('.tree-holder').on('click', 'tr[data-link] a', function (e) { + return $('.tree-holder').on('click', 'tr[data-link] a', function(e) { return e.stopImmediatePropagation(); }); } // eslint-disable-next-line class-methods-use-this setupEntryClick() { - return $('.tree-holder').on('click', 'tr[data-link]', function () { + return $('.tree-holder').on('click', 'tr[data-link]', function() { visitUrl(this.dataset.link, convertPermissionToBoolean(this.dataset.externalLink)); }); } @@ -37,11 +37,15 @@ export default class BuildArtifacts { // We want the tooltip to show if you hover anywhere on the row // But be placed below and in the middle of the file name $('.js-artifact-tree-row') - .on('mouseenter', (e) => { - $(e.currentTarget).find('.js-artifact-tree-tooltip').tooltip('show'); + .on('mouseenter', e => { + $(e.currentTarget) + .find('.js-artifact-tree-tooltip') + .tooltip('show'); }) - .on('mouseleave', (e) => { - $(e.currentTarget).find('.js-artifact-tree-tooltip').tooltip('hide'); + .on('mouseleave', e => { + $(e.currentTarget) + .find('.js-artifact-tree-tooltip') + .tooltip('hide'); }); } } diff --git a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js index b33adff609f..c7a917457f3 100644 --- a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js +++ b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js @@ -7,11 +7,13 @@ import statusCodes from '../lib/utils/http_status'; import VariableList from './ci_variable_list'; function generateErrorBoxContent(errors) { - const errorList = [].concat(errors).map(errorString => ` + const errorList = [].concat(errors).map( + errorString => ` <li> ${_.escape(errorString)} </li> - `); + `, + ); return ` <p> @@ -25,13 +27,7 @@ function generateErrorBoxContent(errors) { // Used for the variable list on CI/CD projects/groups settings page export default class AjaxVariableList { - constructor({ - container, - saveButton, - errorBox, - formField = 'variables', - saveEndpoint, - }) { + constructor({ container, saveButton, errorBox, formField = 'variables', saveEndpoint }) { this.container = container; this.saveButton = saveButton; this.errorBox = errorBox; @@ -51,25 +47,28 @@ export default class AjaxVariableList { } onSaveClicked() { - const loadingIcon = this.saveButton.querySelector('.js-secret-variables-save-loading-icon'); + const loadingIcon = this.saveButton.querySelector('.js-ci-variables-save-loading-icon'); loadingIcon.classList.toggle('hide', false); this.errorBox.classList.toggle('hide', true); // We use this to prevent a user from changing a key before we have a chance // to match it up in `updateRowsWithPersistedVariables` this.variableList.toggleEnableRow(false); - return axios.patch(this.saveEndpoint, { - variables_attributes: this.variableList.getAllData(), - }, { - // We want to be able to process the `res.data` from a 400 error response - // and print the validation messages such as duplicate variable keys - validateStatus: status => ( - status >= statusCodes.OK && - status < statusCodes.MULTIPLE_CHOICES - ) || - status === statusCodes.BAD_REQUEST, - }) - .then((res) => { + return axios + .patch( + this.saveEndpoint, + { + variables_attributes: this.variableList.getAllData(), + }, + { + // We want to be able to process the `res.data` from a 400 error response + // and print the validation messages such as duplicate variable keys + validateStatus: status => + (status >= statusCodes.OK && status < statusCodes.MULTIPLE_CHOICES) || + status === statusCodes.BAD_REQUEST, + }, + ) + .then(res => { loadingIcon.classList.toggle('hide', true); this.variableList.toggleEnableRow(true); @@ -90,18 +89,21 @@ export default class AjaxVariableList { } updateRowsWithPersistedVariables(persistedVariables = []) { - const persistedVariableMap = [].concat(persistedVariables).reduce((variableMap, variable) => ({ - ...variableMap, - [variable.key]: variable, - }), {}); + const persistedVariableMap = [].concat(persistedVariables).reduce( + (variableMap, variable) => ({ + ...variableMap, + [variable.key]: variable, + }), + {}, + ); - this.container.querySelectorAll('.js-row').forEach((row) => { + this.container.querySelectorAll('.js-row').forEach(row => { // If we submitted a row that was destroyed, remove it so we don't try // to destroy it again which would cause a BE error const destroyInput = row.querySelector('.js-ci-variable-input-destroy'); if (convertPermissionToBoolean(destroyInput.value)) { row.remove(); - // Update the ID input so any future edits and `_destroy` will apply on the BE + // Update the ID input so any future edits and `_destroy` will apply on the BE } else { const key = row.querySelector('.js-ci-variable-input-key').value; const persistedVariable = persistedVariableMap[key]; diff --git a/app/assets/javascripts/ci_variable_list/ci_variable_list.js b/app/assets/javascripts/ci_variable_list/ci_variable_list.js index 47efb3a8cee..7bdc18ce03e 100644 --- a/app/assets/javascripts/ci_variable_list/ci_variable_list.js +++ b/app/assets/javascripts/ci_variable_list/ci_variable_list.js @@ -16,10 +16,7 @@ function createEnvironmentItem(value) { } export default class VariableList { - constructor({ - container, - formField, - }) { + constructor({ container, formField }) { this.$container = $(container); this.formField = formField; this.environmentDropdownMap = new WeakMap(); @@ -71,7 +68,7 @@ export default class VariableList { this.initRow(rowEl); }); - this.$container.on('click', '.js-row-remove-button', (e) => { + this.$container.on('click', '.js-row-remove-button', e => { e.preventDefault(); this.removeRow($(e.currentTarget).closest('.js-row')); }); @@ -81,7 +78,7 @@ export default class VariableList { .join(','); // Remove any empty rows except the last row - this.$container.on('blur', inputSelector, (e) => { + this.$container.on('blur', inputSelector, e => { const $row = $(e.currentTarget).closest('.js-row'); if ($row.is(':not(:last-child)') && !this.checkIfRowTouched($row)) { @@ -136,7 +133,7 @@ export default class VariableList { $rowClone.removeAttr('data-is-persisted'); // Reset the inputs to their defaults - Object.keys(this.inputMap).forEach((name) => { + Object.keys(this.inputMap).forEach(name => { const entry = this.inputMap[name]; $rowClone.find(entry.selector).val(entry.default); }); @@ -171,7 +168,7 @@ export default class VariableList { } checkIfRowTouched($row) { - return Object.keys(this.inputMap).some((name) => { + return Object.keys(this.inputMap).some(name => { const entry = this.inputMap[name]; const $el = $row.find(entry.selector); return $el.length && $el.val() !== entry.default; @@ -190,11 +187,14 @@ export default class VariableList { getAllData() { // Ignore the last empty row because we don't want to try persist // a blank variable and run into validation problems. - const validRows = this.$container.find('.js-row').toArray().slice(0, -1); + const validRows = this.$container + .find('.js-row') + .toArray() + .slice(0, -1); - return validRows.map((rowEl) => { + return validRows.map(rowEl => { const resultant = {}; - Object.keys(this.inputMap).forEach((name) => { + Object.keys(this.inputMap).forEach(name => { const entry = this.inputMap[name]; const $input = $(rowEl).find(entry.selector); if ($input.length) { @@ -207,11 +207,16 @@ export default class VariableList { } getEnvironmentValues() { - const valueMap = this.$container.find(this.inputMap.environment_scope.selector).toArray() - .reduce((prevValueMap, envInput) => ({ - ...prevValueMap, - [envInput.value]: envInput.value, - }), {}); + const valueMap = this.$container + .find(this.inputMap.environment_scope.selector) + .toArray() + .reduce( + (prevValueMap, envInput) => ({ + ...prevValueMap, + [envInput.value]: envInput.value, + }), + {}, + ); return Object.keys(valueMap).map(createEnvironmentItem); } diff --git a/app/assets/javascripts/ci_variable_list/native_form_variable_list.js b/app/assets/javascripts/ci_variable_list/native_form_variable_list.js index 7cd5916ac9c..e7111c666a2 100644 --- a/app/assets/javascripts/ci_variable_list/native_form_variable_list.js +++ b/app/assets/javascripts/ci_variable_list/native_form_variable_list.js @@ -2,10 +2,7 @@ import $ from 'jquery'; import VariableList from './ci_variable_list'; // Used for the variable list on scheduled pipeline edit page -export default function setupNativeFormVariableList({ - container, - formField = 'variables', -}) { +export default function setupNativeFormVariableList({ container, formField = 'variables' }) { const $container = $(container); const variableList = new VariableList({ diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 703b0c48934..c7ffb470d4d 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -1,6 +1,6 @@ <script> import _ from 'underscore'; -import helmInstallIllustration from '@gitlab-org/gitlab-svgs/dist/illustrations/kubernetes-installation.svg'; +import helmInstallIllustration from '@gitlab/svgs/dist/illustrations/kubernetes-installation.svg'; import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png'; import gitlabLogo from 'images/cluster_app_logos/gitlab.png'; import helmLogo from 'images/cluster_app_logos/helm.png'; diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index f8e8bf3cc40..e9c580c5dfa 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -84,12 +84,8 @@ export default class ClusterStore { this.state.status = serverState.status; this.state.statusReason = serverState.status_reason; - serverState.applications.forEach((serverAppEntry) => { - const { - name: appId, - status, - status_reason: statusReason, - } = serverAppEntry; + serverState.applications.forEach(serverAppEntry => { + const { name: appId, status, status_reason: statusReason } = serverAppEntry; this.state.applications[appId] = { ...(this.state.applications[appId] || {}), diff --git a/app/assets/javascripts/comment_type_toggle.js b/app/assets/javascripts/comment_type_toggle.js index c74184949df..a259667bb75 100644 --- a/app/assets/javascripts/comment_type_toggle.js +++ b/app/assets/javascripts/comment_type_toggle.js @@ -24,36 +24,44 @@ class CommentTypeToggle { setConfig() { const config = { - InputSetter: [{ - input: this.noteTypeInput, - valueAttribute: 'data-value', - }, - { - input: this.submitButton, - valueAttribute: 'data-submit-text', - }], + InputSetter: [ + { + input: this.noteTypeInput, + valueAttribute: 'data-value', + }, + { + input: this.submitButton, + valueAttribute: 'data-submit-text', + }, + ], }; if (this.closeButton) { - config.InputSetter.push({ - input: this.closeButton, - valueAttribute: 'data-close-text', - }, { - input: this.closeButton, - valueAttribute: 'data-close-text', - inputAttribute: 'data-alternative-text', - }); + config.InputSetter.push( + { + input: this.closeButton, + valueAttribute: 'data-close-text', + }, + { + input: this.closeButton, + valueAttribute: 'data-close-text', + inputAttribute: 'data-alternative-text', + }, + ); } if (this.reopenButton) { - config.InputSetter.push({ - input: this.reopenButton, - valueAttribute: 'data-reopen-text', - }, { - input: this.reopenButton, - valueAttribute: 'data-reopen-text', - inputAttribute: 'data-alternative-text', - }); + config.InputSetter.push( + { + input: this.reopenButton, + valueAttribute: 'data-reopen-text', + }, + { + input: this.reopenButton, + valueAttribute: 'data-reopen-text', + inputAttribute: 'data-alternative-text', + }, + ); } return config; diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js index 30d9b656fec..d4ecfa4aa93 100644 --- a/app/assets/javascripts/commit/image_file.js +++ b/app/assets/javascripts/commit/image_file.js @@ -9,44 +9,60 @@ const viewModes = ['two-up', 'swipe']; export default class ImageFile { constructor(file) { this.file = file; - this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) { - return function(deletedWidth, deletedHeight) { - return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) { - _this.initViewModes(); - - // Load two-up view after images are loaded - // so that we can display the correct width and height information - const $images = $('.two-up.view img', _this.file); - - $images.waitForImages(function() { - _this.initView('two-up'); + this.requestImageInfo( + $('.two-up.view .frame.deleted img', this.file), + (function(_this) { + return function(deletedWidth, deletedHeight) { + return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function( + width, + height, + ) { + _this.initViewModes(); + + // Load two-up view after images are loaded + // so that we can display the correct width and height information + const $images = $('.two-up.view img', _this.file); + + $images.waitForImages(function() { + _this.initView('two-up'); + }); }); - }); - }; - })(this)); + }; + })(this), + ); } initViewModes() { const viewMode = viewModes[0]; $('.view-modes', this.file).removeClass('hide'); - $('.view-modes-menu', this.file).on('click', 'li', (function(_this) { - return function(event) { - if (!$(event.currentTarget).hasClass('active')) { - return _this.activateViewMode(event.currentTarget.className); - } - }; - })(this)); + $('.view-modes-menu', this.file).on( + 'click', + 'li', + (function(_this) { + return function(event) { + if (!$(event.currentTarget).hasClass('active')) { + return _this.activateViewMode(event.currentTarget.className); + } + }; + })(this), + ); return this.activateViewMode(viewMode); } activateViewMode(viewMode) { - $('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active'); - return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) { - return function() { - $(".view." + viewMode, _this.file).fadeIn(200); - return _this.initView(viewMode); - }; - })(this)); + $('.view-modes-menu li', this.file) + .removeClass('active') + .filter('.' + viewMode) + .addClass('active'); + return $('.view:visible:not(.' + viewMode + ')', this.file).fadeOut( + 200, + (function(_this) { + return function() { + $('.view.' + viewMode, _this.file).fadeIn(200); + return _this.initView(viewMode); + }; + })(this), + ); } initView(viewMode) { @@ -63,135 +79,154 @@ export default class ImageFile { $body.css('user-select', 'none'); }); - $body.off('mouseup').off('mousemove').on('mouseup', function() { - dragging = false; - $body.css('user-select', ''); - }) - .on('mousemove', function(e) { - var left; - if (!dragging) return; - - left = e.pageX - ($offsetEl.offset().left + padding); - - callback(e, left); - }); + $body + .off('mouseup') + .off('mousemove') + .on('mouseup', function() { + dragging = false; + $body.css('user-select', ''); + }) + .on('mousemove', function(e) { + var left; + if (!dragging) return; + + left = e.pageX - ($offsetEl.offset().left + padding); + + callback(e, left); + }); } prepareFrames(view) { var maxHeight, maxWidth; maxWidth = 0; maxHeight = 0; - $('.frame', view).each((function(_this) { - return function(index, frame) { - var height, width; - width = $(frame).width(); - height = $(frame).height(); - maxWidth = width > maxWidth ? width : maxWidth; - return maxHeight = height > maxHeight ? height : maxHeight; - }; - })(this)).css({ - width: maxWidth, - height: maxHeight - }); + $('.frame', view) + .each( + (function(_this) { + return function(index, frame) { + var height, width; + width = $(frame).width(); + height = $(frame).height(); + maxWidth = width > maxWidth ? width : maxWidth; + return (maxHeight = height > maxHeight ? height : maxHeight); + }; + })(this), + ) + .css({ + width: maxWidth, + height: maxHeight, + }); return [maxWidth, maxHeight]; } views = { 'two-up': function() { - return $('.two-up.view .wrap', this.file).each((function(_this) { - return function(index, wrap) { - $('img', wrap).each(function() { - var currentWidth; - currentWidth = $(this).width(); - if (currentWidth > availWidth / 2) { - return $(this).width(availWidth / 2); - } - }); - return _this.requestImageInfo($('img', wrap), function(width, height) { - $('.image-info .meta-width', wrap).text(width + "px"); - $('.image-info .meta-height', wrap).text(height + "px"); - return $('.image-info', wrap).removeClass('hide'); - }); - }; - })(this)); + return $('.two-up.view .wrap', this.file).each( + (function(_this) { + return function(index, wrap) { + $('img', wrap).each(function() { + var currentWidth; + currentWidth = $(this).width(); + if (currentWidth > availWidth / 2) { + return $(this).width(availWidth / 2); + } + }); + return _this.requestImageInfo($('img', wrap), function(width, height) { + $('.image-info .meta-width', wrap).text(width + 'px'); + $('.image-info .meta-height', wrap).text(height + 'px'); + return $('.image-info', wrap).removeClass('hide'); + }); + }; + })(this), + ); }, - 'swipe': function() { + swipe() { var maxHeight, maxWidth; maxWidth = 0; maxHeight = 0; - return $('.swipe.view', this.file).each((function(_this) { - return function(index, view) { - var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref; - ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref; - $swipeFrame = $('.swipe-frame', view); - $swipeWrap = $('.swipe-wrap', view); - $swipeBar = $('.swipe-bar', view); - - $swipeFrame.css({ - width: maxWidth + 16, - height: maxHeight + 28 - }); - $swipeWrap.css({ - width: maxWidth + 1, - height: maxHeight + 2 - }); - // Set swipeBar left position to match image frame - $swipeBar.css({ - left: 1 - }); - - wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); - - _this.initDraggable($swipeBar, wrapPadding, function(e, left) { - if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) { - $swipeWrap.width((maxWidth + 1) - left); - $swipeBar.css('left', left); - } - }); - }; - })(this)); + return $('.swipe.view', this.file).each( + (function(_this) { + return function(index, view) { + var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref; + (ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref); + $swipeFrame = $('.swipe-frame', view); + $swipeWrap = $('.swipe-wrap', view); + $swipeBar = $('.swipe-bar', view); + + $swipeFrame.css({ + width: maxWidth + 16, + height: maxHeight + 28, + }); + $swipeWrap.css({ + width: maxWidth + 1, + height: maxHeight + 2, + }); + // Set swipeBar left position to match image frame + $swipeBar.css({ + left: 1, + }); + + wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); + + _this.initDraggable($swipeBar, wrapPadding, function(e, left) { + if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) { + $swipeWrap.width(maxWidth + 1 - left); + $swipeBar.css('left', left); + } + }); + }; + })(this), + ); }, 'onion-skin': function() { var dragTrackWidth, maxHeight, maxWidth; maxWidth = 0; maxHeight = 0; dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width(); - return $('.onion-skin.view', this.file).each((function(_this) { - return function(index, view) { - var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false; - ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref; - $frame = $('.onion-skin-frame', view); - $frameAdded = $('.frame.added', view); - $track = $('.drag-track', view); - $dragger = $('.dragger', $track); - - $frame.css({ - width: maxWidth + 16, - height: maxHeight + 28 - }); - $('.swipe-wrap', view).css({ - width: maxWidth + 1, - height: maxHeight + 2 - }); - $dragger.css({ - left: dragTrackWidth - }); - - $frameAdded.css('opacity', 1); - framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); - - _this.initDraggable($dragger, framePadding, function(e, left) { - var opacity = left / dragTrackWidth; - - if (opacity >= 0 && opacity <= 1) { - $dragger.css('left', left); - $frameAdded.css('opacity', opacity); - } - }); - }; - })(this)); - } - } + return $('.onion-skin.view', this.file).each( + (function(_this) { + return function(index, view) { + var $frame, + $track, + $dragger, + $frameAdded, + framePadding, + ref, + dragging = false; + (ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref); + $frame = $('.onion-skin-frame', view); + $frameAdded = $('.frame.added', view); + $track = $('.drag-track', view); + $dragger = $('.dragger', $track); + + $frame.css({ + width: maxWidth + 16, + height: maxHeight + 28, + }); + $('.swipe-wrap', view).css({ + width: maxWidth + 1, + height: maxHeight + 2, + }); + $dragger.css({ + left: dragTrackWidth, + }); + + $frameAdded.css('opacity', 1); + framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); + + _this.initDraggable($dragger, framePadding, function(e, left) { + var opacity = left / dragTrackWidth; + + if (opacity >= 0 && opacity <= 1) { + $dragger.css('left', left); + $frameAdded.css('opacity', opacity); + } + }); + }; + })(this), + ); + }, + }; requestImageInfo(img, callback) { const domImg = img.get(0); @@ -199,11 +234,14 @@ export default class ImageFile { if (domImg.complete) { return callback.call(this, domImg.naturalWidth, domImg.naturalHeight); } else { - return img.on('load', (function(_this) { - return function() { - return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight); - }; - })(this)); + return img.on( + 'load', + (function(_this) { + return function() { + return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight); + }; + })(this), + ); } } } diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js index 3d89bf1316e..340a93e4e66 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js +++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js @@ -19,11 +19,13 @@ export default () => { const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); if (pipelineTableViewEl) { - // Update MR and Commits tabs - pipelineTableViewEl.addEventListener('update-pipelines-count', (event) => { - if (event.detail.pipelines && + // Update MR and Commits tabs + pipelineTableViewEl.addEventListener('update-pipelines-count', event => { + if ( + event.detail.pipelines && event.detail.pipelines.count && - event.detail.pipelines.count.all) { + event.detail.pipelines.count.all + ) { const badge = document.querySelector('.js-pipelines-mr-count'); badge.textContent = event.detail.pipelines.count.all; diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue index 4849b0fa3db..a2aa3d197e3 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue @@ -1,77 +1,73 @@ <script> - import PipelinesService from '../../pipelines/services/pipelines_service'; - import PipelineStore from '../../pipelines/stores/pipelines_store'; - import pipelinesMixin from '../../pipelines/mixins/pipelines'; +import PipelinesService from '../../pipelines/services/pipelines_service'; +import PipelineStore from '../../pipelines/stores/pipelines_store'; +import pipelinesMixin from '../../pipelines/mixins/pipelines'; - export default { - mixins: [ - pipelinesMixin, - ], - props: { - endpoint: { - type: String, - required: true, - }, - helpPagePath: { - type: String, - required: true, - }, - autoDevopsHelpPath: { - type: String, - required: true, - }, - errorStateSvgPath: { - type: String, - required: true, - }, - viewType: { - type: String, - required: false, - default: 'child', - }, +export default { + mixins: [pipelinesMixin], + props: { + endpoint: { + type: String, + required: true, }, + helpPagePath: { + type: String, + required: true, + }, + autoDevopsHelpPath: { + type: String, + required: true, + }, + errorStateSvgPath: { + type: String, + required: true, + }, + viewType: { + type: String, + required: false, + default: 'child', + }, + }, - data() { - const store = new PipelineStore(); + data() { + const store = new PipelineStore(); - return { - store, - state: store.state, - }; - }, + return { + store, + state: store.state, + }; + }, - computed: { - shouldRenderTable() { - return !this.isLoading && - this.state.pipelines.length > 0 && - !this.hasError; - }, - shouldRenderErrorState() { - return this.hasError && !this.isLoading; - }, + computed: { + shouldRenderTable() { + return !this.isLoading && this.state.pipelines.length > 0 && !this.hasError; }, - created() { - this.service = new PipelinesService(this.endpoint); + shouldRenderErrorState() { + return this.hasError && !this.isLoading; }, - methods: { - successCallback(resp) { - // depending of the endpoint the response can either bring a `pipelines` key or not. - const pipelines = resp.data.pipelines || resp.data; - this.setCommonData(pipelines); + }, + created() { + this.service = new PipelinesService(this.endpoint); + }, + methods: { + successCallback(resp) { + // depending of the endpoint the response can either bring a `pipelines` key or not. + const pipelines = resp.data.pipelines || resp.data; + this.setCommonData(pipelines); - const updatePipelinesEvent = new CustomEvent('update-pipelines-count', { - detail: { - pipelines: resp.data, - }, - }); + const updatePipelinesEvent = new CustomEvent('update-pipelines-count', { + detail: { + pipelines: resp.data, + }, + }); - // notifiy to update the count in tabs - if (this.$el.parentElement) { - this.$el.parentElement.dispatchEvent(updatePipelinesEvent); - } - }, + // notifiy to update the count in tabs + if (this.$el.parentElement) { + this.$el.parentElement.dispatchEvent(updatePipelinesEvent); + } }, - }; + }, +}; </script> <template> <div class="content-list pipelines"> diff --git a/app/assets/javascripts/commit_merge_requests.js b/app/assets/javascripts/commit_merge_requests.js index 102b4ee8463..3a0ab119df6 100644 --- a/app/assets/javascripts/commit_merge_requests.js +++ b/app/assets/javascripts/commit_merge_requests.js @@ -50,7 +50,7 @@ export function createContent(mergeRequests) { if (mergeRequests.length === 0) { $content.text(s__('Commits|No related merge requests found')); } else { - mergeRequests.forEach((mergeRequest) => { + mergeRequests.forEach(mergeRequest => { const $header = createHeader($content.children().length, mergeRequests.length); const $item = createItem(mergeRequest); $content.append($header); @@ -64,8 +64,9 @@ export function createContent(mergeRequests) { export function fetchCommitMergeRequests() { const $container = $('.merge-requests'); - axios.get($container.data('projectCommitPath')) - .then((response) => { + axios + .get($container.data('projectCommitPath')) + .then(response => { const $content = createContent(response.data); $container.html($content); diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index 9a3ea7a55b6..54e2589c707 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -32,22 +32,31 @@ export default class CommitsList { if (search === this.lastSearch) return Promise.resolve(); const commitsUrl = `${form.attr('action')}?${form.serialize()}`; this.content.fadeTo('fast', 0.5); - const params = form.serializeArray().reduce((acc, obj) => Object.assign(acc, { - [obj.name]: obj.value, - }), {}); + const params = form.serializeArray().reduce( + (acc, obj) => + Object.assign(acc, { + [obj.name]: obj.value, + }), + {}, + ); - return axios.get(form.attr('action'), { - params, - }) + return axios + .get(form.attr('action'), { + params, + }) .then(({ data }) => { this.lastSearch = search; this.content.html(data.html); this.content.fadeTo('fast', 1.0); // Change url so if user reload a page - search results are saved - window.history.replaceState({ - page: commitsUrl, - }, document.title, commitsUrl); + window.history.replaceState( + { + page: commitsUrl, + }, + document.title, + commitsUrl, + ); }) .catch(() => { this.content.fadeTo('fast', 1.0); @@ -75,8 +84,15 @@ export default class CommitsList { processedData = $processedData.not(`li.js-commit-header[data-day='${loadedShownDayFirst}']`); // Update commits count in the previous commits header. - commitsCount += Number($(processedData).nextUntil('li.js-commit-header').first().find('li.commit').length); - $commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${pluralize('commit', commitsCount)}`); + commitsCount += Number( + $(processedData) + .nextUntil('li.js-commit-header') + .first() + .find('li.commit').length, + ); + $commitsHeadersLast + .find('span.commits-count') + .text(`${commitsCount} ${pluralize('commit', commitsCount)}`); } localTimeAgo($processedData.find('.js-timeago')); diff --git a/app/assets/javascripts/commons/bootstrap.js b/app/assets/javascripts/commons/bootstrap.js index 50e2949ab55..fba30aea9ae 100644 --- a/app/assets/javascripts/commons/bootstrap.js +++ b/app/assets/javascripts/commons/bootstrap.js @@ -5,6 +5,14 @@ import 'bootstrap'; // custom jQuery functions $.fn.extend({ - disable() { return $(this).prop('disabled', true).addClass('disabled'); }, - enable() { return $(this).prop('disabled', false).removeClass('disabled'); }, + disable() { + return $(this) + .prop('disabled', true) + .addClass('disabled'); + }, + enable() { + return $(this) + .prop('disabled', false) + .removeClass('disabled'); + }, }); diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js index 1411f7ffd5e..e93e1f5ea2c 100644 --- a/app/assets/javascripts/commons/gitlab_ui.js +++ b/app/assets/javascripts/commons/gitlab_ui.js @@ -1,17 +1,7 @@ import Vue from 'vue'; -import { - Pagination, - ProgressBar, - Modal, - LoadingIcon, - ModalDirective, - TooltipDirective, -} from '@gitlab-org/gitlab-ui'; +import { GlProgressBar, GlLoadingIcon, GlTooltipDirective } from '@gitlab-org/gitlab-ui'; -Vue.component('gl-pagination', Pagination); -Vue.component('gl-progress-bar', ProgressBar); -Vue.component('gl-ui-modal', Modal); -Vue.component('gl-loading-icon', LoadingIcon); +Vue.component('gl-progress-bar', GlProgressBar); +Vue.component('gl-loading-icon', GlLoadingIcon); -Vue.directive('gl-modal', ModalDirective); -Vue.directive('gl-tooltip', TooltipDirective); +Vue.directive('gl-tooltip', GlTooltipDirective); diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index b0c85c2572e..1000c310e35 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -13,19 +13,23 @@ function openConfirmDangerModal($form, text) { $submit.disable(); $input.focus(); - $('.js-confirm-danger-input').off('input').on('input', function handleInput() { - const confirmText = rstrip($(this).val()); - if (confirmText === confirmTextMatch) { - $submit.enable(); - } else { - $submit.disable(); - } - }); - $('.js-confirm-danger-submit').off('click').on('click', () => $form.submit()); + $('.js-confirm-danger-input') + .off('input') + .on('input', function handleInput() { + const confirmText = rstrip($(this).val()); + if (confirmText === confirmTextMatch) { + $submit.enable(); + } else { + $submit.disable(); + } + }); + $('.js-confirm-danger-submit') + .off('click') + .on('click', () => $form.submit()); } export default function initConfirmDangerModal() { - $(document).on('click', '.js-confirm-danger', (e) => { + $(document).on('click', '.js-confirm-danger', e => { e.preventDefault(); const $btn = $(e.target); const $form = $btn.closest('form'); diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js index 3a50e73ad85..dff0adba25a 100644 --- a/app/assets/javascripts/contextual_sidebar.js +++ b/app/assets/javascripts/contextual_sidebar.js @@ -20,8 +20,11 @@ export default class ContextualSidebar { } bindEvents() { - document.addEventListener('click', (e) => { - if (!e.target.closest('.nav-sidebar') && (bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md')) { + document.addEventListener('click', e => { + if ( + !e.target.closest('.nav-sidebar') && + (bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md') + ) { this.toggleCollapsedSidebar(true); } }); diff --git a/app/assets/javascripts/create_item_dropdown.js b/app/assets/javascripts/create_item_dropdown.js index 8ef9aa7f529..916b190f469 100644 --- a/app/assets/javascripts/create_item_dropdown.js +++ b/app/assets/javascripts/create_item_dropdown.js @@ -36,7 +36,7 @@ export default class CreateItemDropdown { }, selectable: true, toggleLabel(selected) { - return (selected && 'id' in selected) ? _.escape(selected.title) : this.defaultToggleLabel; + return selected && 'id' in selected ? _.escape(selected.title) : this.defaultToggleLabel; }, fieldName: this.fieldName, text(item) { @@ -46,7 +46,7 @@ export default class CreateItemDropdown { return _.escape(item.id); }, onFilter: this.toggleCreateNewButton.bind(this), - clicked: (options) => { + clicked: options => { options.e.preventDefault(); this.onSelect(); }, @@ -77,9 +77,8 @@ export default class CreateItemDropdown { getData(term, callback) { this.getDataOption(term, (data = []) => { // Ensure the selected item isn't already in the data to avoid duplicates - const alreadyHasSelectedItem = this.selectedItem && data.some(item => - item.id === this.selectedItem.id, - ); + const alreadyHasSelectedItem = + this.selectedItem && data.some(item => item.id === this.selectedItem.id); let uniqueData = data; if (!alreadyHasSelectedItem) { @@ -106,9 +105,7 @@ export default class CreateItemDropdown { if (newValue) { this.selectedItem = this.createNewItemFromValue(newValue); - this.$dropdownContainer - .find('.js-dropdown-create-new-item code') - .text(newValue); + this.$dropdownContainer.find('.js-dropdown-create-new-item code').text(newValue); } this.toggleFooter(!newValue); diff --git a/app/assets/javascripts/create_label.js b/app/assets/javascripts/create_label.js index a999c21b2e9..28ca7d97314 100644 --- a/app/assets/javascripts/create_label.js +++ b/app/assets/javascripts/create_label.js @@ -37,7 +37,7 @@ export default class CreateLabelDropdown { addBinding() { const self = this; - this.$colorSuggestions.on('click', function (e) { + this.$colorSuggestions.on('click', function(e) { const $this = $(this); self.addColorValue(e, $this); }); @@ -47,7 +47,7 @@ export default class CreateLabelDropdown { this.$dropdownBack.on('click', this.resetForm.bind(this)); - this.$cancelButton.on('click', function (e) { + this.$cancelButton.on('click', function(e) { e.preventDefault(); e.stopPropagation(); @@ -79,13 +79,9 @@ export default class CreateLabelDropdown { } resetForm() { - this.$newLabelField - .val('') - .trigger('change'); + this.$newLabelField.val('').trigger('change'); - this.$newColorField - .val('') - .trigger('change'); + this.$newColorField.val('').trigger('change'); this.$colorPreview .css('background-color', '') @@ -97,31 +93,34 @@ export default class CreateLabelDropdown { e.preventDefault(); e.stopPropagation(); - Api.newLabel(this.namespacePath, this.projectPath, { - title: this.$newLabelField.val(), - color: this.$newColorField.val(), - }, (label) => { - this.$newLabelCreateButton.enable(); - - if (label.message) { - let errors; - - if (typeof label.message === 'string') { - errors = label.message; + Api.newLabel( + this.namespacePath, + this.projectPath, + { + title: this.$newLabelField.val(), + color: this.$newColorField.val(), + }, + label => { + this.$newLabelCreateButton.enable(); + + if (label.message) { + let errors; + + if (typeof label.message === 'string') { + errors = label.message; + } else { + errors = Object.keys(label.message) + .map(key => `${humanize(key)} ${label.message[key].join(', ')}`) + .join('<br/>'); + } + + this.$newLabelError.html(errors).show(); } else { - errors = Object.keys(label.message).map(key => - `${humanize(key)} ${label.message[key].join(', ')}`, - ).join('<br/>'); - } + this.$dropdownBack.trigger('click'); - this.$newLabelError - .html(errors) - .show(); - } else { - this.$dropdownBack.trigger('click'); - - $(document).trigger('created.label', label); - } - }); + $(document).trigger('created.label', label); + } + }, + ); } } diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue index aa52f120fe7..3589599986d 100644 --- a/app/assets/javascripts/deploy_keys/components/app.vue +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -95,8 +95,10 @@ export default { .catch(() => new Flash(s__('DeployKeys|Error enabling deploy key'))); }, disableKey(deployKey, callback) { - // eslint-disable-next-line no-alert - if (window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))) { + if ( + // eslint-disable-next-line no-alert + window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?')) + ) { this.service .disableKey(deployKey.id) .then(this.fetchKeys) diff --git a/app/assets/javascripts/deploy_keys/service/index.js b/app/assets/javascripts/deploy_keys/service/index.js index 9dc3b21f6f6..268a37008c5 100644 --- a/app/assets/javascripts/deploy_keys/service/index.js +++ b/app/assets/javascripts/deploy_keys/service/index.js @@ -8,17 +8,14 @@ export default class DeployKeysService { } getKeys() { - return this.axios.get() - .then(response => response.data); + return this.axios.get().then(response => response.data); } enableKey(id) { - return this.axios.put(`${id}/enable`) - .then(response => response.data); + return this.axios.put(`${id}/enable`).then(response => response.data); } disableKey(id) { - return this.axios.put(`${id}/disable`) - .then(response => response.data); + return this.axios.put(`${id}/disable`).then(response => response.data); } } diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index a044fc1ab42..245f1a7c558 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -21,9 +21,12 @@ export default class Diff { }); const tab = document.getElementById('diffs'); - if (!tab || (tab && tab.dataset && tab.dataset.isLocked !== '')) FilesCommentButton.init($diffFile); + if (!tab || (tab && tab.dataset && tab.dataset.isLocked !== '')) + FilesCommentButton.init($diffFile); - const firstFile = $('.files').first().get(0); + const firstFile = $('.files') + .first() + .get(0); const canCreateNote = firstFile && firstFile.hasAttribute('data-can-create-note'); $diffFile.each((index, file) => imageDiffHelper.initImageDiff(file, canCreateNote)); @@ -73,9 +76,10 @@ export default class Diff { const view = file.data('view'); const params = { since, to, bottom, offset, unfold, view }; - axios.get(link, { params }) - .then(({ data }) => $target.parent().replaceWith(data)) - .catch(() => flash(__('An error occurred while loading diff'))); + axios + .get(link, { params }) + .then(({ data }) => $target.parent().replaceWith(data)) + .catch(() => flash(__('An error occurred while loading diff'))); } openAnchoredDiff(cb) { diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index edca45f22f9..59680959bb1 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -41,6 +41,11 @@ export default { required: true, }, }, + data() { + return { + assignedDiscussions: false, + }; + }, computed: { ...mapState({ isLoading: state => state.diffs.isLoading, @@ -58,9 +63,9 @@ export default { plainDiffPath: state => state.diffs.plainDiffPath, emailPatchPath: state => state.diffs.emailPatchPath, }), - ...mapState('diffs', ['showTreeList']), + ...mapState('diffs', ['showTreeList', 'isLoading']), ...mapGetters('diffs', ['isParallelView']), - ...mapGetters(['isNotesFetched', 'discussionsStructuredByLineCode']), + ...mapGetters(['isNotesFetched', 'getNoteableData']), targetBranch() { return { branchName: this.targetBranchName, @@ -147,13 +152,10 @@ export default { } }, setDiscussions() { - if (this.isNotesFetched) { - requestIdleCallback( - () => { - this.assignDiscussionsToDiff(this.discussionsStructuredByLineCode); - }, - { timeout: 1000 }, - ); + if (this.isNotesFetched && !this.assignedDiscussions && !this.isLoading) { + this.assignedDiscussions = true; + + requestIdleCallback(() => this.assignDiscussionsToDiff(), { timeout: 1000 }); } }, adjustView() { diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue index 9bbf62c0eb6..29b5aff0fb1 100644 --- a/app/assets/javascripts/diffs/components/compare_versions.vue +++ b/app/assets/javascripts/diffs/components/compare_versions.vue @@ -40,17 +40,14 @@ export default { comparableDiffs() { return this.mergeRequestDiffs.slice(1); }, - isWhitespaceVisible() { - return !getParameterValues('w')[0]; - }, toggleWhitespaceText() { - if (this.isWhitespaceVisible) { + if (this.isWhitespaceVisible()) { return __('Hide whitespace changes'); } return __('Show whitespace changes'); }, toggleWhitespacePath() { - if (this.isWhitespaceVisible) { + if (this.isWhitespaceVisible()) { return mergeUrlParams({ w: 1 }, window.location.href); } @@ -67,6 +64,9 @@ export default { 'expandAllFiles', 'toggleShowTreeList', ]), + isWhitespaceVisible() { + return getParameterValues('w')[0] !== '1'; + }, }, }; </script> @@ -121,7 +121,7 @@ export default { </a> <a :href="toggleWhitespacePath" - class="btn btn-default" + class="btn btn-default qa-toggle-whitespace" > {{ toggleWhitespaceText }} </a> diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue index f72c7a84e5c..958e57c5652 100644 --- a/app/assets/javascripts/diffs/components/diff_file.vue +++ b/app/assets/javascripts/diffs/components/diff_file.vue @@ -29,7 +29,7 @@ export default { }, computed: { ...mapState('diffs', ['currentDiffFileId']), - ...mapGetters(['isNotesFetched', 'discussionsStructuredByLineCode']), + ...mapGetters(['isNotesFetched']), isCollapsed() { return this.file.collapsed || false; }, @@ -79,7 +79,7 @@ export default { .then(() => { requestIdleCallback( () => { - this.assignDiscussionsToDiff(this.discussionsStructuredByLineCode); + this.assignDiscussionsToDiff(); }, { timeout: 1000 }, ); diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue index cfe4273742f..91052b303a6 100644 --- a/app/assets/javascripts/diffs/components/tree_list.vue +++ b/app/assets/javascripts/diffs/components/tree_list.vue @@ -1,17 +1,30 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; +import { GlTooltipDirective } from '@gitlab-org/gitlab-ui'; +import { convertPermissionToBoolean } from '~/lib/utils/common_utils'; import Icon from '~/vue_shared/components/icon.vue'; import FileRow from '~/vue_shared/components/file_row.vue'; import FileRowStats from './file_row_stats.vue'; +const treeListStorageKey = 'mr_diff_tree_list'; + export default { + directives: { + GlTooltip: GlTooltipDirective, + }, components: { Icon, FileRow, }, data() { + const treeListStored = localStorage.getItem(treeListStorageKey); + const renderTreeList = + treeListStored !== null ? convertPermissionToBoolean(treeListStored) : true; + return { search: '', + renderTreeList, + focusSearch: false, }; }, computed: { @@ -20,15 +33,35 @@ export default { filteredTreeList() { const search = this.search.toLowerCase().trim(); - if (search === '') return this.tree; + if (search === '') return this.renderTreeList ? this.tree : this.allBlobs; return this.allBlobs.filter(f => f.name.toLowerCase().indexOf(search) >= 0); }, + rowDisplayTextKey() { + if (this.renderTreeList && this.search.trim() === '') { + return 'name'; + } + + return 'path'; + }, }, methods: { ...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile']), clearSearch() { this.search = ''; + this.toggleFocusSearch(false); + }, + toggleRenderTreeList(toggle) { + this.renderTreeList = toggle; + localStorage.setItem(treeListStorageKey, this.renderTreeList); + }, + toggleFocusSearch(toggle) { + this.focusSearch = toggle; + }, + blurSearch() { + if (this.search.trim() === '') { + this.toggleFocusSearch(false); + } }, }, FileRowStats, @@ -37,28 +70,67 @@ export default { <template> <div class="tree-list-holder d-flex flex-column"> - <div class="append-bottom-8 position-relative tree-list-search"> - <icon - name="search" - class="position-absolute tree-list-icon" - /> - <input - v-model="search" - :placeholder="s__('MergeRequest|Filter files')" - type="search" - class="form-control" - /> - <button - v-show="search" - :aria-label="__('Clear search')" - type="button" - class="position-absolute tree-list-icon tree-list-clear-icon border-0 p-0" - @click="clearSearch" - > + <div class="append-bottom-8 position-relative tree-list-search d-flex"> + <div class="flex-fill d-flex"> <icon - name="close" + name="search" + class="position-absolute tree-list-icon" + /> + <input + v-model="search" + :placeholder="s__('MergeRequest|Filter files')" + type="search" + class="form-control" + @focus="toggleFocusSearch(true)" + @blur="blurSearch" /> - </button> + <button + v-show="search" + :aria-label="__('Clear search')" + type="button" + class="position-absolute bg-transparent tree-list-icon tree-list-clear-icon border-0 p-0" + @click="clearSearch" + > + <icon + name="close" + /> + </button> + </div> + <div + v-show="!focusSearch" + class="btn-group prepend-left-8 tree-list-view-toggle" + > + <button + v-gl-tooltip.hover + :aria-label="__('List view')" + :title="__('List view')" + :class="{ + active: !renderTreeList + }" + class="btn btn-default pt-0 pb-0 d-flex align-items-center" + type="button" + @click="toggleRenderTreeList(false)" + > + <icon + name="hamburger" + /> + </button> + <button + v-gl-tooltip.hover + :aria-label="__('Tree view')" + :title="__('Tree view')" + :class="{ + active: renderTreeList + }" + class="btn btn-default pt-0 pb-0 d-flex align-items-center" + type="button" + @click="toggleRenderTreeList(true)" + > + <icon + name="file-tree" + /> + </button> + </div> </div> <div class="tree-list-scroll" @@ -72,6 +144,8 @@ export default { :hide-extra-on-tree="true" :extra-component="$options.FileRowStats" :show-changed-icon="true" + :display-text-key="rowDisplayTextKey" + :should-truncate-start="true" @toggleTreeOpen="toggleTreeOpen" @clickFile="scrollToFile" /> diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 1e0b27b538d..ca8ae605cb4 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -5,7 +5,6 @@ import createFlash from '~/flash'; import { s__ } from '~/locale'; import { handleLocationHash, historyPushState } from '~/lib/utils/common_utils'; import { mergeUrlParams, getLocationHash } from '~/lib/utils/url_utility'; -import { reduceDiscussionsToLineCodes } from '../../notes/stores/utils'; import { getDiffPositionByLineCode, getNoteFormData } from './utils'; import * as types from './mutation_types'; import { @@ -36,18 +35,17 @@ export const fetchDiffFiles = ({ state, commit }) => { // This is adding line discussions to the actual lines in the diff tree // once for parallel and once for inline mode -export const assignDiscussionsToDiff = ({ state, commit }, allLineDiscussions) => { +export const assignDiscussionsToDiff = ( + { commit, state, rootState }, + discussions = rootState.notes.discussions, +) => { const diffPositionByLineCode = getDiffPositionByLineCode(state.diffFiles); - Object.values(allLineDiscussions).forEach(discussions => { - if (discussions.length > 0) { - const { fileHash } = discussions[0]; - commit(types.SET_LINE_DISCUSSIONS_FOR_FILE, { - fileHash, - discussions, - diffPositionByLineCode, - }); - } + discussions.filter(discussion => discussion.diff_discussion).forEach(discussion => { + commit(types.SET_LINE_DISCUSSIONS_FOR_FILE, { + discussion, + diffPositionByLineCode, + }); }); }; @@ -190,9 +188,7 @@ export const saveDiffDiscussion = ({ dispatch }, { note, formData }) => { return dispatch('saveNote', postData, { root: true }) .then(result => dispatch('updateDiscussion', result.discussion, { root: true })) - .then(discussion => - dispatch('assignDiscussionsToDiff', reduceDiscussionsToLineCodes([discussion])), - ) + .then(discussion => dispatch('assignDiscussionsToDiff', [discussion])) .catch(() => createFlash(s__('MergeRequests|Saving the comment failed'))); }; diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index 0b4485ecdb5..38a65f111a2 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -90,53 +90,67 @@ export default { })); }, - [types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, discussions, diffPositionByLineCode }) { - const selectedFile = state.diffFiles.find(f => f.fileHash === fileHash); - const firstDiscussion = discussions[0]; - const isDiffDiscussion = firstDiscussion.diff_discussion; - const hasLineCode = firstDiscussion.line_code; - const diffPosition = diffPositionByLineCode[firstDiscussion.line_code]; - - if ( - selectedFile && - isDiffDiscussion && - hasLineCode && - diffPosition && + [types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { discussion, diffPositionByLineCode }) { + const { latestDiff } = state; + + const discussionLineCode = discussion.line_code; + const fileHash = discussion.diff_file.file_hash; + const lineCheck = ({ lineCode }) => + lineCode === discussionLineCode && isDiscussionApplicableToLine({ - discussion: firstDiscussion, - diffPosition, - latestDiff: state.latestDiff, - }) - ) { - const targetLine = selectedFile.parallelDiffLines.find( - line => - (line.left && line.left.lineCode === firstDiscussion.line_code) || - (line.right && line.right.lineCode === firstDiscussion.line_code), - ); - if (targetLine) { - if (targetLine.left && targetLine.left.lineCode === firstDiscussion.line_code) { - Object.assign(targetLine.left, { - discussions, - }); - } else { - Object.assign(targetLine.right, { - discussions, + discussion, + diffPosition: diffPositionByLineCode[lineCode], + latestDiff, + }); + + state.diffFiles = state.diffFiles.map(diffFile => { + if (diffFile.fileHash === fileHash) { + const file = { ...diffFile }; + + if (file.highlightedDiffLines) { + file.highlightedDiffLines = file.highlightedDiffLines.map(line => { + if (lineCheck(line)) { + return { + ...line, + discussions: line.discussions.concat(discussion), + }; + } + + return line; }); } - } - - if (selectedFile.highlightedDiffLines) { - const targetInlineLine = selectedFile.highlightedDiffLines.find( - line => line.lineCode === firstDiscussion.line_code, - ); - if (targetInlineLine) { - Object.assign(targetInlineLine, { - discussions, + if (file.parallelDiffLines) { + file.parallelDiffLines = file.parallelDiffLines.map(line => { + const left = line.left && lineCheck(line.left); + const right = line.right && lineCheck(line.right); + + if (left || right) { + return { + left: { + ...line.left, + discussions: left ? line.left.discussions.concat(discussion) : [], + }, + right: { + ...line.right, + discussions: right && !left ? line.right.discussions.concat(discussion) : [], + }, + }; + } + + return line; }); } + + if (!file.parallelDiffLines || !file.highlightedDiffLines) { + file.discussions = file.discussions.concat(discussion); + } + + return file; } - } + + return diffFile; + }); }, [types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, lineCode }) { diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index d2778bcdf1c..9987fbcb6a7 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -136,7 +136,7 @@ export default function dropzoneInput(form) { // removeAllFiles(true) stops uploading files (if any) // and remove them from dropzone files queue. - $cancelButton.on('click', (e) => { + $cancelButton.on('click', e => { e.preventDefault(); e.stopPropagation(); Dropzone.forElement($formDropzone.get(0)).removeAllFiles(true); @@ -146,8 +146,10 @@ export default function dropzoneInput(form) { // clear dropzone files queue, change status of failed files to undefined, // and add that files to the dropzone files queue again. // addFile() adds file to dropzone files queue and upload it. - $retryLink.on('click', (e) => { - const dropzoneInstance = Dropzone.forElement(e.target.closest('.js-main-target-form').querySelector('.div-dropzone')); + $retryLink.on('click', e => { + const dropzoneInstance = Dropzone.forElement( + e.target.closest('.js-main-target-form').querySelector('.div-dropzone'), + ); const failedFiles = dropzoneInstance.files; e.preventDefault(); @@ -156,7 +158,7 @@ export default function dropzoneInput(form) { // uploading of files that are being uploaded at the moment. dropzoneInstance.removeAllFiles(true); - failedFiles.map((failedFile) => { + failedFiles.map(failedFile => { const file = failedFile; if (file.status === Dropzone.ERROR) { @@ -168,7 +170,7 @@ export default function dropzoneInput(form) { }); }); // eslint-disable-next-line consistent-return - handlePaste = (event) => { + handlePaste = event => { const pasteEvent = event.originalEvent; if (pasteEvent.clipboardData && pasteEvent.clipboardData.items) { const image = isImage(pasteEvent); @@ -182,7 +184,7 @@ export default function dropzoneInput(form) { } }; - isImage = (data) => { + isImage = data => { let i = 0; while (i < data.clipboardData.items.length) { const item = data.clipboardData.items[i]; @@ -203,8 +205,12 @@ export default function dropzoneInput(form) { const caretStart = textarea.selectionStart; const caretEnd = textarea.selectionEnd; const textEnd = $(child).val().length; - const beforeSelection = $(child).val().substring(0, caretStart); - const afterSelection = $(child).val().substring(caretEnd, textEnd); + const beforeSelection = $(child) + .val() + .substring(0, caretStart); + const afterSelection = $(child) + .val() + .substring(caretEnd, textEnd); $(child).val(beforeSelection + formattedText + afterSelection); textarea.setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length); textarea.style.height = `${textarea.scrollHeight}px`; @@ -212,11 +218,11 @@ export default function dropzoneInput(form) { return formTextarea.trigger('input'); }; - addFileToForm = (path) => { + addFileToForm = path => { $(form).append(`<input type="hidden" name="files[]" value="${_.escape(path)}">`); }; - getFilename = (e) => { + getFilename = e => { let value; if (window.clipboardData && window.clipboardData.getData) { value = window.clipboardData.getData('Text'); @@ -231,7 +237,7 @@ export default function dropzoneInput(form) { const closeSpinner = () => $uploadingProgressContainer.addClass('hide'); - const showError = (message) => { + const showError = message => { $uploadingErrorContainer.removeClass('hide'); $uploadingErrorMessage.html(message); }; @@ -252,14 +258,15 @@ export default function dropzoneInput(form) { showSpinner(); closeAlertMessage(); - axios.post(uploadsPath, formData) + axios + .post(uploadsPath, formData) .then(({ data }) => { const md = data.link.markdown; insertToTextArea(filename, md); closeSpinner(); }) - .catch((e) => { + .catch(e => { showError(e.response.data.message); closeSpinner(); }); @@ -267,7 +274,8 @@ export default function dropzoneInput(form) { updateAttachingMessage = (files, messageContainer) => { let attachingMessage; - const filesCount = files.filter(file => file.status === 'uploading' || file.status === 'queued').length; + const filesCount = files.filter(file => file.status === 'uploading' || file.status === 'queued') + .length; // Dinamycally change uploading files text depending on files number in // dropzone files queue. @@ -282,7 +290,10 @@ export default function dropzoneInput(form) { form.find('.markdown-selector').click(function onMarkdownClick(e) { e.preventDefault(); - $(this).closest('.gfm-form').find('.div-dropzone').click(); + $(this) + .closest('.gfm-form') + .find('.div-dropzone') + .click(); formTextarea.focus(); }); diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js index c7b5a35cc14..dbfcf8cc921 100644 --- a/app/assets/javascripts/due_date_select.js +++ b/app/assets/javascripts/due_date_select.js @@ -3,8 +3,7 @@ import Pikaday from 'pikaday'; import dateFormat from 'dateformat'; import { __ } from '~/locale'; import axios from './lib/utils/axios_utils'; -import { timeFor } from './lib/utils/datetime_utility'; -import { parsePikadayDate, pikadayToString } from './lib/utils/datefix'; +import { timeFor, parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility'; import boardsStore from './boards/stores/boards_store'; class DueDateSelect { diff --git a/app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js b/app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js index e9defb62cf8..c5f9fcf6358 100644 --- a/app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js +++ b/app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js @@ -13,9 +13,11 @@ const rainbowCodePoint = 127752; // parseInt('1F308', 16) function isRainbowFlagEmoji(emojiUnicode) { const characters = Array.from(emojiUnicode); // Length 4 because flags are made of 2 characters which are surrogate pairs - return emojiUnicode.length === 4 && + return ( + emojiUnicode.length === 4 && characters[0].codePointAt(0) === baseFlagCodePoint && - characters[1].codePointAt(0) === rainbowCodePoint; + characters[1].codePointAt(0) === rainbowCodePoint + ); } // Chrome <57 renders keycaps oddly @@ -26,22 +28,28 @@ function isKeycapEmoji(emojiUnicode) { } // Check for a skin tone variation emoji which aren't always supported -const tone1 = 127995;// parseInt('1F3FB', 16) -const tone5 = 127999;// parseInt('1F3FF', 16) +const tone1 = 127995; // parseInt('1F3FB', 16) +const tone5 = 127999; // parseInt('1F3FF', 16) function isSkinToneComboEmoji(emojiUnicode) { - return emojiUnicode.length > 2 && Array.from(emojiUnicode).some((char) => { - const cp = char.codePointAt(0); - return cp >= tone1 && cp <= tone5; - }); + return ( + emojiUnicode.length > 2 && + Array.from(emojiUnicode).some(char => { + const cp = char.codePointAt(0); + return cp >= tone1 && cp <= tone5; + }) + ); } // macOS supports most skin tone emoji's but // doesn't support the skin tone versions of horse racing -const horseRacingCodePoint = 127943;// parseInt('1F3C7', 16) +const horseRacingCodePoint = 127943; // parseInt('1F3C7', 16) function isHorceRacingSkinToneComboEmoji(emojiUnicode) { const firstCharacter = Array.from(emojiUnicode)[0]; - return firstCharacter && firstCharacter.codePointAt(0) === horseRacingCodePoint && - isSkinToneComboEmoji(emojiUnicode); + return ( + firstCharacter && + firstCharacter.codePointAt(0) === horseRacingCodePoint && + isSkinToneComboEmoji(emojiUnicode) + ); } // Check for `family_*`, `kiss_*`, `couple_*` @@ -52,7 +60,7 @@ const personEndCodePoint = 128105; // parseInt('1F469', 16) function isPersonZwjEmoji(emojiUnicode) { let hasPersonEmoji = false; let hasZwj = false; - Array.from(emojiUnicode).forEach((character) => { + Array.from(emojiUnicode).forEach(character => { const cp = character.codePointAt(0); if (cp === zwj) { hasZwj = true; @@ -80,10 +88,7 @@ function checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) { // in `isEmojiUnicodeSupported` logic function checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) { const isSkinToneResult = isSkinToneComboEmoji(emojiUnicode); - return ( - (unicodeSupportMap.skinToneModifier && isSkinToneResult) || - !isSkinToneResult - ); + return (unicodeSupportMap.skinToneModifier && isSkinToneResult) || !isSkinToneResult; } // Helper func so we don't have to run `isHorceRacingSkinToneComboEmoji` twice @@ -91,8 +96,7 @@ function checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) { function checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) { const isHorseRacingSkinToneResult = isHorceRacingSkinToneComboEmoji(emojiUnicode); return ( - (unicodeSupportMap.horseRacing && isHorseRacingSkinToneResult) || - !isHorseRacingSkinToneResult + (unicodeSupportMap.horseRacing && isHorseRacingSkinToneResult) || !isHorseRacingSkinToneResult ); } @@ -100,10 +104,7 @@ function checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnico // in `isEmojiUnicodeSupported` logic function checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode) { const isPersonZwjResult = isPersonZwjEmoji(emojiUnicode); - return ( - (unicodeSupportMap.personZwj && isPersonZwjResult) || - !isPersonZwjResult - ); + return (unicodeSupportMap.personZwj && isPersonZwjResult) || !isPersonZwjResult; } // Takes in a support map and determines whether @@ -111,16 +112,20 @@ function checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode) { // // Combines all the edge case tests into a one-stop shop method function isEmojiUnicodeSupported(unicodeSupportMap = {}, emojiUnicode, unicodeVersion) { - const isOlderThanChrome57 = unicodeSupportMap.meta && unicodeSupportMap.meta.isChrome && + const isOlderThanChrome57 = + unicodeSupportMap.meta && + unicodeSupportMap.meta.isChrome && unicodeSupportMap.meta.chromeVersion < 57; // For comments about each scenario, see the comments above each individual respective function - return unicodeSupportMap[unicodeVersion] && + return ( + unicodeSupportMap[unicodeVersion] && !(isOlderThanChrome57 && isKeycapEmoji(emojiUnicode)) && checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) && checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) && checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) && - checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode); + checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode) + ); } export { diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index bb9c139727e..b62a5bb1940 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -15,7 +15,7 @@ import CommitComponent from '../../vue_shared/components/commit.vue'; import eventHub from '../event_hub'; /** - * Envrionment Item Component + * Environment Item Component * * Renders a table row for each environment. */ @@ -60,7 +60,7 @@ export default { computed: { /** - * Verifies if `last_deployment` key exists in the current Envrionment. + * Verifies if `last_deployment` key exists in the current Environment. * This key is required to render most of the html - this method works has * an helper. * diff --git a/app/assets/javascripts/environments/components/environment_monitoring.vue b/app/assets/javascripts/environments/components/environment_monitoring.vue index a0797b594cb..26bec125445 100644 --- a/app/assets/javascripts/environments/components/environment_monitoring.vue +++ b/app/assets/javascripts/environments/components/environment_monitoring.vue @@ -2,14 +2,14 @@ /** * Renders the Monitoring (Metrics) link in environments table. */ -import { Button } from '@gitlab-org/gitlab-ui'; +import { GlButton } from '@gitlab-org/gitlab-ui'; import Icon from '~/vue_shared/components/icon.vue'; import tooltip from '../../vue_shared/directives/tooltip'; export default { components: { Icon, - 'gl-button': Button, + GlButton, }, directives: { tooltip, diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue index e2ecf426e64..557b2062c64 100644 --- a/app/assets/javascripts/environments/components/environments_app.vue +++ b/app/assets/javascripts/environments/components/environments_app.vue @@ -1,94 +1,92 @@ <script> - import Flash from '../../flash'; - import { s__ } from '../../locale'; - import emptyState from './empty_state.vue'; - import eventHub from '../event_hub'; - import environmentsMixin from '../mixins/environments_mixin'; - import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin'; - import StopEnvironmentModal from './stop_environment_modal.vue'; +import Flash from '../../flash'; +import { s__ } from '../../locale'; +import emptyState from './empty_state.vue'; +import eventHub from '../event_hub'; +import environmentsMixin from '../mixins/environments_mixin'; +import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin'; +import StopEnvironmentModal from './stop_environment_modal.vue'; - export default { - components: { - emptyState, - StopEnvironmentModal, - }, +export default { + components: { + emptyState, + StopEnvironmentModal, + }, - mixins: [ - CIPaginationMixin, - environmentsMixin, - ], + mixins: [CIPaginationMixin, environmentsMixin], - props: { - endpoint: { - type: String, - required: true, - }, - canCreateEnvironment: { - type: Boolean, - required: true, - }, - canCreateDeployment: { - type: Boolean, - required: true, - }, - canReadEnvironment: { - type: Boolean, - required: true, - }, - cssContainerClass: { - type: String, - required: true, - }, - newEnvironmentPath: { - type: String, - required: true, - }, - helpPagePath: { - type: String, - required: true, - }, + props: { + endpoint: { + type: String, + required: true, }, - - created() { - eventHub.$on('toggleFolder', this.toggleFolder); + canCreateEnvironment: { + type: Boolean, + required: true, }, - - beforeDestroy() { - eventHub.$off('toggleFolder'); + canCreateDeployment: { + type: Boolean, + required: true, + }, + canReadEnvironment: { + type: Boolean, + required: true, + }, + cssContainerClass: { + type: String, + required: true, + }, + newEnvironmentPath: { + type: String, + required: true, }, + helpPagePath: { + type: String, + required: true, + }, + }, + + created() { + eventHub.$on('toggleFolder', this.toggleFolder); + }, - methods: { - toggleFolder(folder) { - this.store.toggleFolder(folder); + beforeDestroy() { + eventHub.$off('toggleFolder'); + }, - if (!folder.isOpen) { - this.fetchChildEnvironments(folder, true); - } - }, + methods: { + toggleFolder(folder) { + this.store.toggleFolder(folder); - fetchChildEnvironments(folder, showLoader = false) { - this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader); + if (!folder.isOpen) { + this.fetchChildEnvironments(folder, true); + } + }, - this.service.getFolderContent(folder.folder_path) - .then(response => this.store.setfolderContent(folder, response.data.environments)) - .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false)) - .catch(() => { - Flash(s__('Environments|An error occurred while fetching the environments.')); - this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false); - }); - }, + fetchChildEnvironments(folder, showLoader = false) { + this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader); + + this.service + .getFolderContent(folder.folder_path) + .then(response => this.store.setfolderContent(folder, response.data.environments)) + .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false)) + .catch(() => { + Flash(s__('Environments|An error occurred while fetching the environments.')); + this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false); + }); + }, - successCallback(resp) { - this.saveData(resp); + successCallback(resp) { + this.saveData(resp); - // We need to verify if any folder is open to also update it - const openFolders = this.store.getOpenFolders(); - if (openFolders.length) { - openFolders.forEach(folder => this.fetchChildEnvironments(folder)); - } - }, + // We need to verify if any folder is open to also update it + const openFolders = this.store.getOpenFolders(); + if (openFolders.length) { + openFolders.forEach(folder => this.fetchChildEnvironments(folder)); + } }, - }; + }, +}; </script> <template> <div :class="cssContainerClass"> diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js index 5ce9225a4bb..5808a2d4afa 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js +++ b/app/assets/javascripts/environments/stores/environments_store.js @@ -34,14 +34,14 @@ export default class EnvironmentsStore { * @returns {Array} */ storeEnvironments(environments = []) { - const filteredEnvironments = environments.map((env) => { - const oldEnvironmentState = this.state.environments - .find((element) => { - if (env.latest) { - return element.id === env.latest.id; - } - return element.id === env.id; - }) || {}; + const filteredEnvironments = environments.map(env => { + const oldEnvironmentState = + this.state.environments.find(element => { + if (env.latest) { + return element.id === env.latest.id; + } + return element.id === env.id; + }) || {}; let filtered = {}; @@ -101,11 +101,11 @@ export default class EnvironmentsStore { } /** - * Toggles folder open property for the given folder. - * - * @param {Object} folder - * @return {Array} - */ + * Toggles folder open property for the given folder. + * + * @param {Object} folder + * @return {Array} + */ toggleFolder(folder) { return this.updateEnvironmentProp(folder, 'isOpen', !folder.isOpen); } @@ -119,7 +119,7 @@ export default class EnvironmentsStore { * @return {Object} */ setfolderContent(folder, environments) { - const updatedEnvironments = environments.map((env) => { + const updatedEnvironments = environments.map(env => { let updated = env; if (env.latest) { @@ -148,7 +148,7 @@ export default class EnvironmentsStore { updateEnvironmentProp(environment, prop, newValue) { const { environments } = this.state; - const updatedEnvironments = environments.map((env) => { + const updatedEnvironments = environments.map(env => { const updateEnv = Object.assign({}, env); if (env.id === environment.id) { updateEnv[prop] = newValue; diff --git a/app/assets/javascripts/experimental_flags.js b/app/assets/javascripts/experimental_flags.js index 1d60847147b..42b3fb8c6da 100644 --- a/app/assets/javascripts/experimental_flags.js +++ b/app/assets/javascripts/experimental_flags.js @@ -2,7 +2,7 @@ import $ from 'jquery'; import Cookies from 'js-cookie'; export default () => { - $('.js-experiment-feature-toggle').on('change', (e) => { + $('.js-experiment-feature-toggle').on('change', e => { const el = e.target; Cookies.set(el.name, el.value, { diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js index 6a4874e1ab8..3233f5c4f71 100644 --- a/app/assets/javascripts/files_comment_button.js +++ b/app/assets/javascripts/files_comment_button.js @@ -25,13 +25,15 @@ export default { if (!this.userCanCreateNote) { // data-can-create-note is an empty string when true, otherwise undefined - this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('canCreateNote') === ''; + this.userCanCreateNote = + $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('canCreateNote') === ''; } this.isParallelView = Cookies.get('diff_view') === 'parallel'; if (this.userCanCreateNote) { - $diffFile.on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e)) + $diffFile + .on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e)) .on('mouseleave', LINE_COLUMN_CLASSES, e => this.hideButton(this.isParallelView, e)); } }, @@ -64,9 +66,11 @@ export default { }, validateButtonParent(buttonParentElement) { - return !buttonParentElement.classList.contains(EMPTY_CELL_CLASS) && + return ( + !buttonParentElement.classList.contains(EMPTY_CELL_CLASS) && !buttonParentElement.classList.contains(UNFOLDABLE_LINE_CLASS) && !buttonParentElement.classList.contains(NO_COMMENT_CLASS) && - !buttonParentElement.parentNode.classList.contains(DIFF_EXPANDED_CLASS); + !buttonParentElement.parentNode.classList.contains(DIFF_EXPANDED_CLASS) + ); }, }; diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js index b17ba3c21db..64b09c8b62c 100644 --- a/app/assets/javascripts/filterable_list.js +++ b/app/assets/javascripts/filterable_list.js @@ -65,12 +65,15 @@ export default class FilterableList { this.isBusy = true; - return axios.get(this.getFilterEndpoint(), { - params, - }).then((res) => { - this.onFilterSuccess(res, params); - this.onFilterComplete(); - }).catch(() => this.onFilterComplete()); + return axios + .get(this.getFilterEndpoint(), { + params, + }) + .then(res => { + this.onFilterSuccess(res, params); + this.onFilterComplete(); + }) + .catch(() => this.onFilterComplete()); } onFilterSuccess(response, queryData) { @@ -81,9 +84,13 @@ export default class FilterableList { // Change url so if user reload a page - search results are saved const currentPath = this.getPagePath(queryData); - return window.history.replaceState({ - page: currentPath, - }, document.title, currentPath); + return window.history.replaceState( + { + page: currentPath, + }, + document.title, + currentPath, + ); } onFilterComplete() { diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js index d36f38a70b5..d5027590bb7 100644 --- a/app/assets/javascripts/filtered_search/dropdown_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_user.js @@ -39,8 +39,9 @@ export default class DropdownUser extends FilteredSearchDropdown { } itemClicked(e) { - super.itemClicked(e, - selected => selected.querySelector('.dropdown-light-content').innerText.trim()); + super.itemClicked(e, selected => + selected.querySelector('.dropdown-light-content').innerText.trim(), + ); } renderContent(forceShowList = false) { @@ -68,7 +69,7 @@ export default class DropdownUser extends FilteredSearchDropdown { // Removes the first character if it is a quotation so that we can search // with multiple words - if (value[0] === '"' || value[0] === '\'') { + if (value[0] === '"' || value[0] === "'") { value = value.slice(1); } diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js index 4eb67ff7649..146d3ba963c 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js @@ -85,7 +85,7 @@ export default class FilteredSearchDropdown { } dispatchInputEvent() { - // Propogate input change to FilteredSearchDropdownManager + // Propagate input change to FilteredSearchDropdownManager // so that it can determine which dropdowns to open this.input.dispatchEvent( new CustomEvent('input', { diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index cd3d532c958..57ec6603d80 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -108,7 +108,7 @@ export default class FilteredSearchDropdownManager { }, }; - supportedTokens.forEach((type) => { + supportedTokens.forEach(type => { if (availableMappings[type]) { allowedMappings[type] = availableMappings[type]; } @@ -142,10 +142,7 @@ export default class FilteredSearchDropdownManager { } static addWordToInput(tokenName, tokenValue = '', clicked = false, options = {}) { - const { - uppercaseTokenName = false, - capitalizeTokenValue = false, - } = options; + const { uppercaseTokenName = false, capitalizeTokenValue = false } = options; const input = FilteredSearchContainer.container.querySelector('.filtered-search'); FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue, { uppercaseTokenName, @@ -164,13 +161,16 @@ export default class FilteredSearchDropdownManager { updateDropdownOffset(key) { // Always align dropdown with the input field - let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left; + let offset = + this.filteredSearchInput.getBoundingClientRect().left - + this.container.querySelector('.scroll-container').getBoundingClientRect().left; const maxInputWidth = 240; const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth; // Make sure offset never exceeds the input container - const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth; + const offsetMaxWidth = + this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth; if (offsetMaxWidth < offset) { offset = offsetMaxWidth; } @@ -196,8 +196,7 @@ export default class FilteredSearchDropdownManager { const glArguments = Object.assign({}, defaultArguments, extraArguments); // Passing glArguments to `new glClass(<arguments>)` - mappingKey.reference = - new (Function.prototype.bind.apply(glClass, [null, glArguments]))(); + mappingKey.reference = new (Function.prototype.bind.apply(glClass, [null, glArguments]))(); } if (firstLoad) { @@ -224,8 +223,8 @@ export default class FilteredSearchDropdownManager { } const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase()); - const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key - && this.mapping[match.key]; + const shouldOpenFilterDropdown = + match && this.currentDropdown !== match.key && this.mapping[match.key]; const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint'; if (shouldOpenFilterDropdown || shouldOpenHintDropdown) { @@ -236,8 +235,10 @@ export default class FilteredSearchDropdownManager { setDropdown() { const query = DropdownUtils.getSearchQuery(true); - const { lastToken, searchToken } = - this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys()); + const { lastToken, searchToken } = this.tokenizer.processTokens( + query, + this.filteredSearchTokenKeys.getKeys(), + ); if (this.currentDropdown) { this.updateCurrentDropdownOffset(); diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 54533ebb70d..4a2af02b40a 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -1,8 +1,5 @@ import _ from 'underscore'; -import { - getParameterByName, - getUrlParamsArray, -} from '~/lib/utils/common_utils'; +import { getParameterByName, getUrlParamsArray } from '~/lib/utils/common_utils'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; import { visitUrl } from '../lib/utils/url_utility'; import Flash from '../flash'; @@ -48,24 +45,28 @@ export default class FilteredSearchManager { isLocalStorageAvailable: RecentSearchesService.isAvailable(), allowedKeys: this.filteredSearchTokenKeys.getKeys(), }); - this.searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown'); - const fullPath = this.searchHistoryDropdownElement ? - this.searchHistoryDropdownElement.dataset.fullPath : 'project'; + this.searchHistoryDropdownElement = document.querySelector( + '.js-filtered-search-history-dropdown', + ); + const fullPath = this.searchHistoryDropdownElement + ? this.searchHistoryDropdownElement.dataset.fullPath + : 'project'; const recentSearchesKey = `${fullPath}-${this.recentsStorageKeyNames[this.page]}`; this.recentSearchesService = new RecentSearchesService(recentSearchesKey); } setup() { // Fetch recent searches from localStorage - this.fetchingRecentSearchesPromise = this.recentSearchesService.fetch() - .catch((error) => { + this.fetchingRecentSearchesPromise = this.recentSearchesService + .fetch() + .catch(error => { if (error.name === 'RecentSearchesServiceError') return undefined; // eslint-disable-next-line no-new new Flash('An error occurred while parsing recent searches'); // Gracefully fail to empty array return []; }) - .then((searches) => { + .then(searches => { if (!searches) { return; } @@ -120,7 +121,7 @@ export default class FilteredSearchManager { if (this.stateFilters) { this.searchStateWrapper = this.searchState.bind(this); - this.applyToStateFilters((filterEl) => { + this.applyToStateFilters(filterEl => { filterEl.addEventListener('click', this.searchStateWrapper); }); } @@ -128,14 +129,14 @@ export default class FilteredSearchManager { unbindStateEvents() { if (this.stateFilters) { - this.applyToStateFilters((filterEl) => { + this.applyToStateFilters(filterEl => { filterEl.removeEventListener('click', this.searchStateWrapper); }); } } applyToStateFilters(callback) { - this.stateFilters.querySelectorAll('a[data-state]').forEach((filterEl) => { + this.stateFilters.querySelectorAll('a[data-state]').forEach(filterEl => { if (this.states.indexOf(filterEl.dataset.state) > -1) { callback(filterEl); } @@ -207,7 +208,7 @@ export default class FilteredSearchManager { let backspaceCount = 0; // closure for keeping track of the number of backspace keystrokes - return (e) => { + return e => { // 8 = Backspace Key // 46 = Delete Key if (e.keyCode === 8 || e.keyCode === 46) { @@ -274,8 +275,12 @@ export default class FilteredSearchManager { const isElementInDynamicFilterDropdown = e.target.closest('.filter-dropdown') !== null; const isElementInStaticFilterDropdown = e.target.closest('ul[data-dropdown]') !== null; - if (!isElementInFilteredSearch && !isElementInDynamicFilterDropdown && - !isElementInStaticFilterDropdown && inputContainer) { + if ( + !isElementInFilteredSearch && + !isElementInDynamicFilterDropdown && + !isElementInStaticFilterDropdown && + inputContainer + ) { inputContainer.classList.remove('focus'); } } @@ -368,7 +373,7 @@ export default class FilteredSearchManager { const removeElements = []; - [].forEach.call(this.tokensContainer.children, (t) => { + [].forEach.call(this.tokensContainer.children, t => { let canClearToken = t.classList.contains('js-visual-token'); if (canClearToken) { @@ -381,7 +386,7 @@ export default class FilteredSearchManager { } }); - removeElements.forEach((el) => { + removeElements.forEach(el => { el.parentElement.removeChild(el); }); @@ -397,13 +402,14 @@ export default class FilteredSearchManager { handleInputVisualToken() { const input = this.filteredSearchInput; - const { tokens, searchToken } - = this.tokenizer.processTokens(input.value, this.filteredSearchTokenKeys.getKeys()); - const { isLastVisualTokenValid } - = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + const { tokens, searchToken } = this.tokenizer.processTokens( + input.value, + this.filteredSearchTokenKeys.getKeys(), + ); + const { isLastVisualTokenValid } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); if (isLastVisualTokenValid) { - tokens.forEach((t) => { + tokens.forEach(t => { input.value = input.value.replace(`${t.key}:${t.symbol}${t.value}`, ''); FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`, { uppercaseTokenName: this.filteredSearchTokenKeys.shouldUppercaseTokenName(t.key), @@ -453,15 +459,17 @@ export default class FilteredSearchManager { saveCurrentSearchQuery() { // Don't save before we have fetched the already saved searches - this.fetchingRecentSearchesPromise.then(() => { - const searchQuery = DropdownUtils.getSearchQuery(); - if (searchQuery.length > 0) { - const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery); - this.recentSearchesService.save(resultantSearches); - } - }).catch(() => { - // https://gitlab.com/gitlab-org/gitlab-ce/issues/30821 - }); + this.fetchingRecentSearchesPromise + .then(() => { + const searchQuery = DropdownUtils.getSearchQuery(); + if (searchQuery.length > 0) { + const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery); + this.recentSearchesService.save(resultantSearches); + } + }) + .catch(() => { + // https://gitlab.com/gitlab-org/gitlab-ce/issues/30821 + }); } // allows for modifying params array when a param can't be included in the URL (e.g. Service Desk) @@ -475,7 +483,7 @@ export default class FilteredSearchManager { const usernameParams = this.getUsernameParams(); let hasFilteredSearch = false; - params.forEach((p) => { + params.forEach(p => { const split = p.split('='); const keyParam = decodeURIComponent(split[0]); const value = split[1]; @@ -486,11 +494,9 @@ export default class FilteredSearchManager { if (condition) { hasFilteredSearch = true; const canEdit = this.canEdit && this.canEdit(condition.tokenKey); - FilteredSearchVisualTokens.addFilterVisualToken( - condition.tokenKey, - condition.value, - { canEdit }, - ); + FilteredSearchVisualTokens.addFilterVisualToken(condition.tokenKey, condition.value, { + canEdit, + }); } else { // Sanitize value since URL converts spaces into + // Replace before decode so that we know what was originally + versus the encoded + @@ -510,7 +516,7 @@ export default class FilteredSearchManager { if (sanitizedValue.indexOf(' ') !== -1) { // Prefer ", but use ' if required - quotationsToUse = sanitizedValue.indexOf('"') === -1 ? '"' : '\''; + quotationsToUse = sanitizedValue.indexOf('"') === -1 ? '"' : "'"; } hasFilteredSearch = true; @@ -531,7 +537,9 @@ export default class FilteredSearchManager { hasFilteredSearch = true; const tokenName = 'assignee'; const canEdit = this.canEdit && this.canEdit(tokenName); - FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, { canEdit }); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, { + canEdit, + }); } } else if (!match && keyParam === 'author_id') { const id = parseInt(value, 10); @@ -539,7 +547,9 @@ export default class FilteredSearchManager { hasFilteredSearch = true; const tokenName = 'author'; const canEdit = this.canEdit && this.canEdit(tokenName); - FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, { canEdit }); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, { + canEdit, + }); } } else if (!match && keyParam === 'search') { hasFilteredSearch = true; @@ -580,9 +590,11 @@ export default class FilteredSearchManager { const currentState = state || getParameterByName('state') || 'opened'; paths.push(`state=${currentState}`); - tokens.forEach((token) => { - const condition = this.filteredSearchTokenKeys - .searchByConditionKeyValue(token.key, token.value.toLowerCase()); + tokens.forEach(token => { + const condition = this.filteredSearchTokenKeys.searchByConditionKeyValue( + token.key, + token.value.toLowerCase(), + ); const tokenConfig = this.filteredSearchTokenKeys.searchByKey(token.key) || {}; const { param } = tokenConfig; @@ -601,8 +613,10 @@ export default class FilteredSearchManager { tokenValue = tokenValue.toLowerCase(); } - if ((tokenValue[0] === '\'' && tokenValue[tokenValue.length - 1] === '\'') || - (tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"')) { + if ( + (tokenValue[0] === "'" && tokenValue[tokenValue.length - 1] === "'") || + (tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"') + ) { tokenValue = tokenValue.slice(1, tokenValue.length - 1); } @@ -613,7 +627,10 @@ export default class FilteredSearchManager { }); if (searchToken) { - const sanitized = searchToken.split(' ').map(t => encodeURIComponent(t)).join('+'); + const sanitized = searchToken + .split(' ') + .map(t => encodeURIComponent(t)) + .join('+'); paths.push(`search=${sanitized}`); } @@ -630,7 +647,7 @@ export default class FilteredSearchManager { const usernamesById = {}; try { const attribute = this.filteredSearchInput.getAttribute('data-username-params'); - JSON.parse(attribute).forEach((user) => { + JSON.parse(attribute).forEach(user => { usernamesById[user.id] = user.username; }); } catch (e) { diff --git a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js index b70125c80ca..bb0ecb8efe7 100644 --- a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js +++ b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js @@ -58,17 +58,22 @@ export const alternativeTokenKeys = [ export const conditions = [ { - url: 'assignee_id=0', + url: 'assignee_id=None', tokenKey: 'assignee', value: 'none', }, { - url: 'milestone_title=No+Milestone', + url: 'assignee_id=Any', + tokenKey: 'assignee', + value: 'any', + }, + { + url: 'milestone_title=None', tokenKey: 'milestone', value: 'none', }, { - url: 'milestone_title=Any+Milestone', + url: 'milestone_title=Any', tokenKey: 'milestone', value: 'any', }, @@ -87,6 +92,16 @@ export const conditions = [ tokenKey: 'label', value: 'none', }, + { + url: 'my_reaction_emoji=None', + tokenKey: 'my-reaction', + value: 'none', + }, + { + url: 'my_reaction_emoji=Any', + tokenKey: 'my-reaction', + value: 'any', + }, ]; const IssuableFilteredSearchTokenKeys = new FilteredSearchTokenKeys( diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js index a29de9ae899..c2397842125 100644 --- a/app/assets/javascripts/flash.js +++ b/app/assets/javascripts/flash.js @@ -8,14 +8,19 @@ const hideFlash = (flashEl, fadeTransition = true) => { }); } - flashEl.addEventListener('transitionend', () => { - flashEl.remove(); - window.dispatchEvent(new Event('resize')); - if (document.body.classList.contains('flash-shown')) document.body.classList.remove('flash-shown'); - }, { - once: true, - passive: true, - }); + flashEl.addEventListener( + 'transitionend', + () => { + flashEl.remove(); + window.dispatchEvent(new Event('resize')); + if (document.body.classList.contains('flash-shown')) + document.body.classList.remove('flash-shown'); + }, + { + once: true, + passive: true, + }, + ); if (!fadeTransition) flashEl.dispatchEvent(new Event('transitionend')); }; @@ -30,12 +35,14 @@ const createAction = config => ` </a> `; -const createFlashEl = (message, type, isInContentWrapper = false) => ` +const createFlashEl = (message, type, isFixedLayout = false) => ` <div class="flash-${type}" > <div - class="flash-text ${isInContentWrapper ? 'container-fluid container-limited' : ''}" + class="flash-text ${ + isFixedLayout ? 'container-fluid container-limited limit-container-width' : '' + }" > ${_.escape(message)} </div> @@ -69,12 +76,15 @@ const createFlash = function createFlash( addBodyClass = false, ) { const flashContainer = parent.querySelector('.flash-container'); + const navigation = parent.querySelector('.content'); if (!flashContainer) return null; - const isInContentWrapper = flashContainer.parentNode.classList.contains('content-wrapper'); + const isFixedLayout = navigation + ? navigation.parentNode.classList.contains('container-limited') + : true; - flashContainer.innerHTML = createFlashEl(message, type, isInContentWrapper); + flashContainer.innerHTML = createFlashEl(message, type, isFixedLayout); const flashEl = flashContainer.querySelector(`.flash-${type}`); removeFlashClickListener(flashEl, fadeTransition); @@ -83,7 +93,9 @@ const createFlash = function createFlash( flashEl.innerHTML += createAction(actionConfig); if (actionConfig.clickHandler) { - flashEl.querySelector('.flash-action').addEventListener('click', e => actionConfig.clickHandler(e)); + flashEl + .querySelector('.flash-action') + .addEventListener('click', e => actionConfig.clickHandler(e)); } } @@ -94,11 +106,5 @@ const createFlash = function createFlash( return flashContainer; }; -export { - createFlash as default, - createFlashEl, - createAction, - hideFlash, - removeFlashClickListener, -}; +export { createFlash as default, createFlashEl, createAction, hideFlash, removeFlashClickListener }; window.Flash = createFlash; diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index f820f0dc3f0..3ac00c51df4 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -11,9 +11,13 @@ let sidebar; export const mousePos = []; -export const setSidebar = (el) => { sidebar = el; }; +export const setSidebar = el => { + sidebar = el; +}; export const getOpenMenu = () => currentOpenMenu; -export const setOpenMenu = (menu = null) => { currentOpenMenu = menu; }; +export const setOpenMenu = (menu = null) => { + currentOpenMenu = menu; +}; export const slope = (a, b) => (b.y - a.y) / (b.x - a.x); @@ -21,9 +25,10 @@ let headerHeight = 50; export const getHeaderHeight = () => headerHeight; -export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-collapsed-desktop'); +export const isSidebarCollapsed = () => + sidebar && sidebar.classList.contains('sidebar-collapsed-desktop'); -export const canShowActiveSubItems = (el) => { +export const canShowActiveSubItems = el => { if (el.classList.contains('active') && !isSidebarCollapsed()) { return false; } @@ -31,7 +36,10 @@ export const canShowActiveSubItems = (el) => { return true; }; -export const canShowSubItems = () => bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md' || bp.getBreakpointSize() === 'lg'; +export const canShowSubItems = () => + bp.getBreakpointSize() === 'sm' || + bp.getBreakpointSize() === 'md' || + bp.getBreakpointSize() === 'lg'; export const getHideSubItemsInterval = () => { if (!currentOpenMenu || !mousePos.length) return 0; @@ -41,11 +49,12 @@ export const getHideSubItemsInterval = () => { const currentMousePosY = currentMousePos.y; const [menuTop, menuBottom] = menuCornerLocs; - if (currentMousePosY < menuTop.y || - currentMousePosY > menuBottom.y) return 0; + if (currentMousePosY < menuTop.y || currentMousePosY > menuBottom.y) return 0; - if (slope(prevMousePos, menuBottom) < slope(currentMousePos, menuBottom) && - slope(prevMousePos, menuTop) > slope(currentMousePos, menuTop)) { + if ( + slope(prevMousePos, menuBottom) < slope(currentMousePos, menuBottom) && + slope(prevMousePos, menuTop) > slope(currentMousePos, menuTop) + ) { return HIDE_INTERVAL_TIMEOUT; } @@ -56,11 +65,12 @@ export const calculateTop = (boundingRect, outerHeight) => { const windowHeight = window.innerHeight; const bottomOverflow = windowHeight - (boundingRect.top + outerHeight); - return bottomOverflow < 0 ? (boundingRect.top - outerHeight) + boundingRect.height : - boundingRect.top; + return bottomOverflow < 0 + ? boundingRect.top - outerHeight + boundingRect.height + : boundingRect.top; }; -export const hideMenu = (el) => { +export const hideMenu = el => { if (!el) return; const parentEl = el.parentNode; @@ -101,7 +111,7 @@ export const moveSubItemsToPosition = (el, subItems) => { } }; -export const showSubLevelItems = (el) => { +export const showSubLevelItems = el => { const subItems = el.querySelector('.sidebar-sub-level-items'); const isIconOnly = subItems && subItems.classList.contains('is-fly-out-only'); @@ -128,16 +138,20 @@ export const mouseEnterTopItems = (el, timeout = getHideSubItemsInterval()) => { }, timeout); }; -export const mouseLeaveTopItem = (el) => { +export const mouseLeaveTopItem = el => { const subItems = el.querySelector('.sidebar-sub-level-items'); - if (!canShowSubItems() || !canShowActiveSubItems(el) || - (subItems && subItems === currentOpenMenu)) return; + if ( + !canShowSubItems() || + !canShowActiveSubItems(el) || + (subItems && subItems === currentOpenMenu) + ) + return; el.classList.remove(IS_OVER_CLASS); }; -export const documentMouseMove = (e) => { +export const documentMouseMove = e => { mousePos.push({ x: e.clientX, y: e.clientY, @@ -146,7 +160,7 @@ export const documentMouseMove = (e) => { if (mousePos.length > 6) mousePos.shift(); }; -export const subItemsMouseLeave = (relatedTarget) => { +export const subItemsMouseLeave = relatedTarget => { clearTimeout(timeoutId); if (relatedTarget && !relatedTarget.closest(`.${IS_OVER_CLASS}`)) { @@ -174,7 +188,7 @@ export default () => { headerHeight = document.querySelector('.nav-sidebar').offsetTop; - items.forEach((el) => { + items.forEach(el => { const subItems = el.querySelector('.sidebar-sub-level-items'); if (subItems) { diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 95636a9ccdd..00b3d283570 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -94,7 +94,7 @@ class GfmAutoComplete { ...this.getDefaultCallbacks(), beforeSave(commands) { if (GfmAutoComplete.isLoading(commands)) return commands; - return $.map(commands, (c) => { + return $.map(commands, c => { let search = c.name; if (c.aliases.length > 0) { search = `${search} ${c.aliases.join(' ')}`; @@ -167,7 +167,7 @@ class GfmAutoComplete { callbacks: { ...this.getDefaultCallbacks(), beforeSave(members) { - return $.map(members, (m) => { + return $.map(members, m => { let title = ''; if (m.username == null) { return m; @@ -178,7 +178,9 @@ class GfmAutoComplete { } const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase(); - const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar avatar-inline center s26"/>`; + const imgAvatar = `<img src="${m.avatar_url}" alt="${ + m.username + }" class="avatar avatar-inline center s26"/>`; const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`; return { @@ -201,7 +203,7 @@ class GfmAutoComplete { displayTpl(value) { let tmpl = GfmAutoComplete.Loading.template; if (value.title != null) { - tmpl = GfmAutoComplete.Issues.template; + tmpl = GfmAutoComplete.Issues.templateFunction(value.id, value.title); } return tmpl; }, @@ -211,7 +213,7 @@ class GfmAutoComplete { callbacks: { ...this.getDefaultCallbacks(), beforeSave(issues) { - return $.map(issues, (i) => { + return $.map(issues, i => { if (i.title == null) { return i; } @@ -244,7 +246,7 @@ class GfmAutoComplete { callbacks: { ...this.getDefaultCallbacks(), beforeSave(milestones) { - return $.map(milestones, (m) => { + return $.map(milestones, m => { if (m.title == null) { return m; } @@ -267,7 +269,7 @@ class GfmAutoComplete { displayTpl(value) { let tmpl = GfmAutoComplete.Loading.template; if (value.title != null) { - tmpl = GfmAutoComplete.Issues.template; + tmpl = GfmAutoComplete.Issues.templateFunction(value.id, value.title); } return tmpl; }, @@ -277,7 +279,7 @@ class GfmAutoComplete { callbacks: { ...this.getDefaultCallbacks(), beforeSave(merges) { - return $.map(merges, (m) => { + return $.map(merges, m => { if (m.title == null) { return m; } @@ -324,13 +326,20 @@ class GfmAutoComplete { }, matcher(flag, subtext) { const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers); - const subtextNodes = subtext.split(/\n+/g).pop().split(GfmAutoComplete.regexSubtext); + const subtextNodes = subtext + .split(/\n+/g) + .pop() + .split(GfmAutoComplete.regexSubtext); // Check if ~ is followed by '/label', '/relabel' or '/unlabel' commands. - command = subtextNodes.find((node) => { - if (node === LABEL_COMMAND.LABEL || - node === LABEL_COMMAND.RELABEL || - node === LABEL_COMMAND.UNLABEL) { return node; } + command = subtextNodes.find(node => { + if ( + node === LABEL_COMMAND.LABEL || + node === LABEL_COMMAND.RELABEL || + node === LABEL_COMMAND.UNLABEL + ) { + return node; + } return null; }); @@ -370,7 +379,7 @@ class GfmAutoComplete { displayTpl(value) { let tmpl = GfmAutoComplete.Loading.template; if (value.title != null) { - tmpl = GfmAutoComplete.Issues.template; + tmpl = GfmAutoComplete.Issues.templateFunction(value.id, value.title); } return tmpl; }, @@ -380,7 +389,7 @@ class GfmAutoComplete { callbacks: { ...this.getDefaultCallbacks(), beforeSave(snippets) { - return $.map(snippets, (m) => { + return $.map(snippets, m => { if (m.title == null) { return m; } @@ -458,13 +467,17 @@ class GfmAutoComplete { this.loadData($input, at, validEmojiNames); GfmAutoComplete.glEmojiTag = glEmojiTag; }) - .catch(() => { this.isLoadingData[at] = false; }); + .catch(() => { + this.isLoadingData[at] = false; + }); } else if (dataSource) { AjaxCache.retrieve(dataSource, true) - .then((data) => { + .then(data => { this.loadData($input, at, data); }) - .catch(() => { this.isLoadingData[at] = false; }); + .catch(() => { + this.isLoadingData[at] = false; + }); } else { this.isLoadingData[at] = false; } @@ -497,15 +510,16 @@ class GfmAutoComplete { } const loadingState = GfmAutoComplete.defaultLoadingData[0]; - return dataToInspect && - (dataToInspect === loadingState || dataToInspect.name === loadingState); + return dataToInspect && (dataToInspect === loadingState || dataToInspect.name === loadingState); } static defaultMatcher(flag, subtext, controllers) { // The below is taken from At.js source // Tweaked to commands to start without a space only if char before is a non-word character // https://github.com/ichord/At.js - const atSymbolsWithBar = Object.keys(controllers).join('|').replace(/[$]/, '\\$&'); + const atSymbolsWithBar = Object.keys(controllers) + .join('|') + .replace(/[$]/, '\\$&'); const atSymbolsWithoutBar = Object.keys(controllers).join(''); const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop(); const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); @@ -513,7 +527,10 @@ class GfmAutoComplete { const accentAChar = decodeURI('%C3%80'); const accentYChar = decodeURI('%C3%BF'); - const regexp = new RegExp(`^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`, 'gi'); + const regexp = new RegExp( + `^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`, + 'gi', + ); return regexp.exec(targetSubtext); } @@ -552,13 +569,15 @@ GfmAutoComplete.Members = { template: '<li>${avatarTag} ${username} <small>${title}</small></li>', }; GfmAutoComplete.Labels = { - // eslint-disable-next-line no-template-curly-in-string - template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>', + template: + // eslint-disable-next-line no-template-curly-in-string + '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>', }; // Issues, MergeRequests and Snippets GfmAutoComplete.Issues = { - // eslint-disable-next-line no-template-curly-in-string - template: '<li><small>${id}</small> ${title}</li>', + templateFunction(id, title) { + return `<li><small>${id}</small> ${_.escape(title)}</li>`; + }, }; // Milestones GfmAutoComplete.Milestones = { @@ -566,7 +585,8 @@ GfmAutoComplete.Milestones = { template: '<li>${title}</li>', }; GfmAutoComplete.Loading = { - template: '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>', + template: + '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>', }; export default GfmAutoComplete; diff --git a/app/assets/javascripts/gl_field_error.js b/app/assets/javascripts/gl_field_error.js index 87c6e37b9fb..a5b8c357e8a 100644 --- a/app/assets/javascripts/gl_field_error.js +++ b/app/assets/javascripts/gl_field_error.js @@ -116,7 +116,8 @@ export default class GlFieldError { this.form.focusOnFirstInvalid.apply(this.form); // For UX, wait til after first invalid submission to check each keyup - this.inputElement.off('keyup.fieldValidator') + this.inputElement + .off('keyup.fieldValidator') .on('keyup.fieldValidator', this.updateValidity.bind(this)); } diff --git a/app/assets/javascripts/gl_field_errors.js b/app/assets/javascripts/gl_field_errors.js index b9c51045b1d..d5d5954ce6a 100644 --- a/app/assets/javascripts/gl_field_errors.js +++ b/app/assets/javascripts/gl_field_errors.js @@ -16,16 +16,19 @@ export default class GlFieldErrors { initValidators() { // register selectors here as needed const validateSelectors = [':text', ':password', '[type=email]'] - .map(selector => `input${selector}`).join(','); + .map(selector => `input${selector}`) + .join(','); - this.state.inputs = this.form.find(validateSelectors).toArray() + this.state.inputs = this.form + .find(validateSelectors) + .toArray() .filter(input => !input.classList.contains(customValidationFlag)) .map(input => new GlFieldError({ input, formErrors: this })); this.form.on('submit', GlFieldErrors.catchInvalidFormSubmit); } - /* Neccessary to prevent intercept and override invalid form submit + /* Necessary to prevent intercept and override invalid form submit * because Safari & iOS quietly allow form submission when form is invalid * and prevents disabling of invalid submit button by application.js */ @@ -42,7 +45,7 @@ export default class GlFieldErrors { /* Public method for triggering validity updates manually */ updateFormValidityState() { - this.state.inputs.forEach((field) => { + this.state.inputs.forEach(field => { if (field.state.submitted) { field.updateValidity(); } @@ -50,8 +53,9 @@ export default class GlFieldErrors { } focusOnFirstInvalid() { - const firstInvalid = this.state.inputs - .filter(input => !input.inputDomElement.validity.valid)[0]; + const firstInvalid = this.state.inputs.filter( + input => !input.inputDomElement.validity.valid, + )[0]; firstInvalid.inputElement.focus(); } } diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index e672284a2d0..f842d2d74db 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -39,7 +39,10 @@ export default class GLForm { this.form.find('.div-dropzone').remove(); this.form.addClass('gfm-form'); // remove notify commit author checkbox for non-commit notes - gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button, .js-note-new-discussion')); + gl.utils.disableButtonIfEmptyField( + this.form.find('.js-note-text'), + this.form.find('.js-comment-button, .js-note-new-discussion'), + ); this.autoComplete = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources); this.autoComplete.setup(this.form.find('.js-gfm-input'), this.enableGFM); dropzoneInput(this.form); @@ -55,11 +58,9 @@ export default class GLForm { } setupAutosize() { - this.textarea.off('autosize:resized') - .on('autosize:resized', this.setHeightData.bind(this)); + this.textarea.off('autosize:resized').on('autosize:resized', this.setHeightData.bind(this)); - this.textarea.off('mouseup.autosize') - .on('mouseup.autosize', this.destroyAutosize.bind(this)); + this.textarea.off('mouseup.autosize').on('mouseup.autosize', this.destroyAutosize.bind(this)); setTimeout(() => { autosize(this.textarea); @@ -91,10 +92,14 @@ export default class GLForm { addEventListeners() { this.textarea.on('focus', function focusTextArea() { - $(this).closest('.md-area').addClass('is-focused'); + $(this) + .closest('.md-area') + .addClass('is-focused'); }); this.textarea.on('blur', function blurTextArea() { - $(this).closest('.md-area').removeClass('is-focused'); + $(this) + .closest('.md-area') + .removeClass('is-focused'); }); } } diff --git a/app/assets/javascripts/group.js b/app/assets/javascripts/group.js index 4365305c168..903c838e266 100644 --- a/app/assets/javascripts/group.js +++ b/app/assets/javascripts/group.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import { slugifyWithHyphens } from './lib/utils/text_utility'; export default class Group { constructor() { @@ -7,17 +8,18 @@ export default class Group { this.updateHandler = this.update.bind(this); this.resetHandler = this.reset.bind(this); if (this.groupName.val() === '') { - this.groupPath.on('keyup', this.updateHandler); - this.groupName.on('keydown', this.resetHandler); + this.groupName.on('keyup', this.updateHandler); + this.groupPath.on('keydown', this.resetHandler); } } update() { - this.groupName.val(this.groupPath.val()); + const slug = slugifyWithHyphens(this.groupName.val()); + this.groupPath.val(slug); } reset() { - this.groupPath.off('keyup', this.updateHandler); - this.groupName.off('keydown', this.resetHandler); + this.groupName.off('keyup', this.updateHandler); + this.groupPath.off('keydown', this.resetHandler); } } diff --git a/app/assets/javascripts/group_avatar.js b/app/assets/javascripts/group_avatar.js index beaac61e887..dcda625f587 100644 --- a/app/assets/javascripts/group_avatar.js +++ b/app/assets/javascripts/group_avatar.js @@ -7,8 +7,9 @@ export default function groupAvatar() { }); $('.js-group-avatar-input').on('change', function onChangeAvatarInput() { const form = $(this).closest('form'); - // eslint-disable-next-line no-useless-escape - const filename = $(this).val().replace(/^.*[\\\/]/, ''); + const filename = $(this) + .val() + .replace(/^.*[\\\/]/, ''); // eslint-disable-line no-useless-escape return form.find('.js-avatar-filename').text(filename); }); } diff --git a/app/assets/javascripts/group_label_subscription.js b/app/assets/javascripts/group_label_subscription.js index d33e3a37580..9b74560f914 100644 --- a/app/assets/javascripts/group_label_subscription.js +++ b/app/assets/javascripts/group_label_subscription.js @@ -23,7 +23,8 @@ export default class GroupLabelSubscription { event.preventDefault(); const url = this.$unsubscribeButtons.attr('data-url'); - axios.post(url) + axios + .post(url) .then(() => { this.toggleSubscriptionButtons(); this.$unsubscribeButtons.removeAttr('data-url'); @@ -39,7 +40,8 @@ export default class GroupLabelSubscription { this.$unsubscribeButtons.attr('data-url', url); - axios.post(url) + axios + .post(url) .then(() => GroupLabelSubscription.setNewTooltip($btn)) .then(() => this.toggleSubscriptionButtons()) .catch(() => flash(__('There was an error when subscribing to this label.'))); @@ -58,6 +60,8 @@ export default class GroupLabelSubscription { const newTitle = tooltipTitles[type]; $('.js-unsubscribe-button', $button.closest('.label-actions-list')) - .tooltip('hide').attr('title', newTitle).tooltip('_fixTitle'); + .tooltip('hide') + .attr('title', newTitle) + .tooltip('_fixTitle'); } } diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue index 87ab5480c15..829924ba63c 100644 --- a/app/assets/javascripts/groups/components/item_stats.vue +++ b/app/assets/javascripts/groups/components/item_stats.vue @@ -1,44 +1,44 @@ <script> - import icon from '~/vue_shared/components/icon.vue'; - import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; - import { - ITEM_TYPE, - VISIBILITY_TYPE_ICON, - GROUP_VISIBILITY_TYPE, - PROJECT_VISIBILITY_TYPE, - } from '../constants'; - import itemStatsValue from './item_stats_value.vue'; +import icon from '~/vue_shared/components/icon.vue'; +import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import { + ITEM_TYPE, + VISIBILITY_TYPE_ICON, + GROUP_VISIBILITY_TYPE, + PROJECT_VISIBILITY_TYPE, +} from '../constants'; +import itemStatsValue from './item_stats_value.vue'; - export default { - components: { - icon, - timeAgoTooltip, - itemStatsValue, +export default { + components: { + icon, + timeAgoTooltip, + itemStatsValue, + }, + props: { + item: { + type: Object, + required: true, }, - props: { - item: { - type: Object, - required: true, - }, + }, + computed: { + visibilityIcon() { + return VISIBILITY_TYPE_ICON[this.item.visibility]; }, - computed: { - visibilityIcon() { - return VISIBILITY_TYPE_ICON[this.item.visibility]; - }, - visibilityTooltip() { - if (this.item.type === ITEM_TYPE.GROUP) { - return GROUP_VISIBILITY_TYPE[this.item.visibility]; - } - return PROJECT_VISIBILITY_TYPE[this.item.visibility]; - }, - isProject() { - return this.item.type === ITEM_TYPE.PROJECT; - }, - isGroup() { - return this.item.type === ITEM_TYPE.GROUP; - }, + visibilityTooltip() { + if (this.item.type === ITEM_TYPE.GROUP) { + return GROUP_VISIBILITY_TYPE[this.item.visibility]; + } + return PROJECT_VISIBILITY_TYPE[this.item.visibility]; }, - }; + isProject() { + return this.item.type === ITEM_TYPE.PROJECT; + }, + isGroup() { + return this.item.type === ITEM_TYPE.GROUP; + }, + }, +}; </script> <template> diff --git a/app/assets/javascripts/groups/components/item_stats_value.vue b/app/assets/javascripts/groups/components/item_stats_value.vue index ef9f2bca76c..c542ca946d3 100644 --- a/app/assets/javascripts/groups/components/item_stats_value.vue +++ b/app/assets/javascripts/groups/components/item_stats_value.vue @@ -1,52 +1,52 @@ <script> - import tooltip from '~/vue_shared/directives/tooltip'; - import icon from '~/vue_shared/components/icon.vue'; +import tooltip from '~/vue_shared/directives/tooltip'; +import icon from '~/vue_shared/components/icon.vue'; - export default { - components: { - icon, +export default { + components: { + icon, + }, + directives: { + tooltip, + }, + props: { + title: { + type: String, + required: false, + default: '', }, - directives: { - tooltip, + cssClass: { + type: String, + required: false, + default: '', }, - props: { - title: { - type: String, - required: false, - default: '', - }, - cssClass: { - type: String, - required: false, - default: '', - }, - iconName: { - type: String, - required: true, - }, - tooltipPlacement: { - type: String, - required: false, - default: 'bottom', - }, - /** - * value could either be number or string - * as `memberCount` is always passed as string - * while `subgroupCount` & `projectCount` - * are always number - */ - value: { - type: [Number, String], - required: false, - default: '', - }, + iconName: { + type: String, + required: true, }, - computed: { - isValuePresent() { - return this.value !== ''; - }, + tooltipPlacement: { + type: String, + required: false, + default: 'bottom', }, - }; + /** + * value could either be number or string + * as `memberCount` is always passed as string + * while `subgroupCount` & `projectCount` + * are always number + */ + value: { + type: [Number, String], + required: false, + default: '', + }, + }, + computed: { + isValuePresent() { + return this.value !== ''; + }, + }, +}; </script> <template> diff --git a/app/assets/javascripts/groups/new_group_child.js b/app/assets/javascripts/groups/new_group_child.js index a120d501e35..012177479c6 100644 --- a/app/assets/javascripts/groups/new_group_child.js +++ b/app/assets/javascripts/groups/new_group_child.js @@ -37,20 +37,22 @@ export default class NewGroupChild { getDroplabConfig() { return { - InputSetter: [{ - input: this.newGroupChildButton, - valueAttribute: 'data-value', - inputAttribute: 'data-action', - }, { - input: this.newGroupChildButton, - valueAttribute: 'data-text', - }], + InputSetter: [ + { + input: this.newGroupChildButton, + valueAttribute: 'data-value', + inputAttribute: 'data-action', + }, + { + input: this.newGroupChildButton, + valueAttribute: 'data-text', + }, + ], }; } bindEvents() { - this.newGroupChildButton - .addEventListener('click', this.onClickNewGroupChildButton.bind(this)); + this.newGroupChildButton.addEventListener('click', this.onClickNewGroupChildButton.bind(this)); } onClickNewGroupChildButton(e) { diff --git a/app/assets/javascripts/groups/store/groups_store.js b/app/assets/javascripts/groups/store/groups_store.js index 4a7569078a1..16f95d5a0cc 100644 --- a/app/assets/javascripts/groups/store/groups_store.js +++ b/app/assets/javascripts/groups/store/groups_store.js @@ -17,13 +17,14 @@ export default class GroupsStore { } setSearchedGroups(rawGroups) { - const formatGroups = groups => groups.map((group) => { - const formattedGroup = this.formatGroupItem(group); - if (formattedGroup.children && formattedGroup.children.length) { - formattedGroup.children = formatGroups(formattedGroup.children); - } - return formattedGroup; - }); + const formatGroups = groups => + groups.map(group => { + const formattedGroup = this.formatGroupItem(group); + if (formattedGroup.children && formattedGroup.children.length) { + formattedGroup.children = formatGroups(formattedGroup.children); + } + return formattedGroup; + }); if (rawGroups && rawGroups.length) { this.state.groups = formatGroups(rawGroups); @@ -62,10 +63,10 @@ export default class GroupsStore { formatGroupItem(rawGroupItem) { const groupChildren = rawGroupItem.children || []; - const groupIsOpen = (groupChildren.length > 0) || false; - const childrenCount = this.hideProjects ? - rawGroupItem.subgroup_count : - rawGroupItem.children_count; + const groupIsOpen = groupChildren.length > 0 || false; + const childrenCount = this.hideProjects + ? rawGroupItem.subgroup_count + : rawGroupItem.children_count; return { id: rawGroupItem.id, diff --git a/app/assets/javascripts/groups/transfer_dropdown.js b/app/assets/javascripts/groups/transfer_dropdown.js index e0eb118ddf7..26510fcdb2a 100644 --- a/app/assets/javascripts/groups/transfer_dropdown.js +++ b/app/assets/javascripts/groups/transfer_dropdown.js @@ -22,7 +22,7 @@ export default class TransferDropdown { search: { fields: ['text'] }, data: extraOptions.concat(this.data), text: item => item.text, - clicked: (options) => { + clicked: options => { const { e } = options; e.preventDefault(); this.assignSelected(options.selectedObj); diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index e37fc5c4be6..b4a3037c1b7 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -23,7 +23,7 @@ export default function groupsSelect() { axios[params.type.toLowerCase()](params.url, { params: params.data, }) - .then((res) => { + .then(res => { const results = res.data || []; const headers = normalizeHeaders(res.headers); const currentPage = parseInt(headers['X-PAGE'], 10) || 0; @@ -36,7 +36,8 @@ export default function groupsSelect() { more, }, }); - }).catch(params.error); + }) + .catch(params.error); }, data(search, page) { return { @@ -68,7 +69,9 @@ export default function groupsSelect() { } }, formatResult(object) { - return `<div class='group-result'> <div class='group-name'>${object.full_name}</div> <div class='group-path'>${object.full_path}</div> </div>`; + return `<div class='group-result'> <div class='group-name'>${ + object.full_name + }</div> <div class='group-path'>${object.full_path}</div> </div>`; }, formatSelection(object) { return object.full_name; diff --git a/app/assets/javascripts/helpers/avatar_helper.js b/app/assets/javascripts/helpers/avatar_helper.js index d3b1d0f11fd..35ac7b2629c 100644 --- a/app/assets/javascripts/helpers/avatar_helper.js +++ b/app/assets/javascripts/helpers/avatar_helper.js @@ -19,7 +19,9 @@ export function renderIdenticon(entity, options = {}) { const bgClass = getIdenticonBackgroundClass(entity.id); const title = getIdenticonTitle(entity.name); - return `<div class="avatar identicon ${_.escape(sizeClass)} ${_.escape(bgClass)}">${_.escape(title)}</div>`; + return `<div class="avatar identicon ${_.escape(sizeClass)} ${_.escape(bgClass)}">${_.escape( + title, + )}</div>`; } export function renderAvatar(entity, options = {}) { diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue index ad6151e3bf6..0a368f6558c 100644 --- a/app/assets/javascripts/ide/components/ide.vue +++ b/app/assets/javascripts/ide/components/ide.vue @@ -43,7 +43,7 @@ export default { 'currentProjectId', 'errorMessage', ]), - ...mapGetters(['activeFile', 'hasChanges', 'someUncommitedChanges', 'isCommitModeActive']), + ...mapGetters(['activeFile', 'hasChanges', 'someUncommittedChanges', 'isCommitModeActive']), }, mounted() { window.onbeforeunload = e => this.onBeforeUnload(e); @@ -63,7 +63,7 @@ export default { onBeforeUnload(e = {}) { const returnValue = __('Are you sure you want to lose unsaved changes?'); - if (!this.someUncommitedChanges) return undefined; + if (!this.someUncommittedChanges) return undefined; Object.assign(e, { returnValue, diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue index dc84ee12f1e..364ab9426e0 100644 --- a/app/assets/javascripts/ide/components/ide_side_bar.vue +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -1,6 +1,6 @@ <script> import { mapState, mapGetters } from 'vuex'; -import { SkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; import IdeTree from './ide_tree.vue'; import ResizablePanel from './resizable_panel.vue'; import ActivityBar from './activity_bar.vue'; @@ -13,7 +13,7 @@ import { activityBarViews } from '../constants'; export default { components: { - SkeletonLoading, + GlSkeletonLoading, ResizablePanel, ActivityBar, CommitSection, @@ -25,11 +25,11 @@ export default { }, computed: { ...mapState(['loading', 'currentActivityView', 'changedFiles', 'stagedFiles', 'lastCommitMsg']), - ...mapGetters(['currentProject', 'someUncommitedChanges']), + ...mapGetters(['currentProject', 'someUncommittedChanges']), showSuccessMessage() { return ( this.currentActivityView === activityBarViews.edit && - (this.lastCommitMsg && !this.someUncommitedChanges) + (this.lastCommitMsg && !this.someUncommittedChanges) ); }, }, @@ -50,7 +50,7 @@ export default { :key="n" class="multi-file-loading-container" > - <skeleton-loading /> + <gl-skeleton-loading /> </div> </div> </template> diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue index e88f01fb4f4..d2ff55a4ee3 100644 --- a/app/assets/javascripts/ide/components/ide_tree_list.vue +++ b/app/assets/javascripts/ide/components/ide_tree_list.vue @@ -1,7 +1,7 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; import Icon from '~/vue_shared/components/icon.vue'; -import { SkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; import FileRow from '~/vue_shared/components/file_row.vue'; import NavDropdown from './nav_dropdown.vue'; import FileRowExtra from './file_row_extra.vue'; @@ -9,7 +9,7 @@ import FileRowExtra from './file_row_extra.vue'; export default { components: { Icon, - SkeletonLoading, + GlSkeletonLoading, NavDropdown, FileRow, }, @@ -51,7 +51,7 @@ export default { :key="n" class="multi-file-loading-container" > - <skeleton-loading /> + <gl-skeleton-loading /> </div> </template> <template v-else> diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue index 0a2681b7a1e..b670b0355b7 100644 --- a/app/assets/javascripts/ide/components/pipelines/list.vue +++ b/app/assets/javascripts/ide/components/pipelines/list.vue @@ -25,7 +25,7 @@ export default { ...mapState('pipelines', ['isLoadingPipeline', 'latestPipeline', 'stages', 'isLoadingJobs']), ciLintText() { return sprintf( - __('You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}'), + __('You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}.'), { linkStart: `<a href="${_.escape(this.currentProject.web_url)}/-/ci/lint">`, linkEnd: '</a>', diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index d3b24c5b793..5e86876c1c1 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -27,10 +27,10 @@ export default { 'unusedSeal', ]), ...mapState('commit', ['commitMessage', 'submitCommitLoading']), - ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges', 'activeFile']), + ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommittedChanges', 'activeFile']), ...mapGetters('commit', ['discardDraftButtonDisabled']), showStageUnstageArea() { - return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal); + return !!(this.someUncommittedChanges || this.lastCommitMsg || !this.unusedSeal); }, activeFileKey() { return this.activeFile ? this.activeFile.key : null; diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js index 709748fb530..8ad85074d6b 100644 --- a/app/assets/javascripts/ide/stores/getters.js +++ b/app/assets/javascripts/ide/stores/getters.js @@ -63,7 +63,7 @@ export const isEditModeActive = state => state.currentActivityView === activityB export const isCommitModeActive = state => state.currentActivityView === activityBarViews.commit; export const isReviewModeActive = state => state.currentActivityView === activityBarViews.review; -export const someUncommitedChanges = state => +export const someUncommittedChanges = state => !!(state.changedFiles.length || state.stagedFiles.length); export const getChangesInFolder = state => path => { diff --git a/app/assets/javascripts/image_diff/image_diff.js b/app/assets/javascripts/image_diff/image_diff.js index fab0255c378..3587f073a00 100644 --- a/app/assets/javascripts/image_diff/image_diff.js +++ b/app/assets/javascripts/image_diff/image_diff.js @@ -60,8 +60,10 @@ export default class ImageDiff { } renderBadge(discussionEl, index) { - const imageBadge = imageDiffHelper - .generateBadgeFromDiscussionDOM(this.imageFrameEl, discussionEl); + const imageBadge = imageDiffHelper.generateBadgeFromDiscussionDOM( + this.imageFrameEl, + discussionEl, + ); this.imageBadges.push(imageBadge); diff --git a/app/assets/javascripts/image_diff/init_discussion_tab.js b/app/assets/javascripts/image_diff/init_discussion_tab.js index 2f16c6ef115..dbe4c06a4e9 100644 --- a/app/assets/javascripts/image_diff/init_discussion_tab.js +++ b/app/assets/javascripts/image_diff/init_discussion_tab.js @@ -8,5 +8,6 @@ export default () => { const diffFileEls = document.querySelectorAll('.timeline-content .diff-file.js-image-file'); [...diffFileEls].forEach(diffFileEl => - imageDiffHelper.initImageDiff(diffFileEl, canCreateNote, renderCommentBadge)); + imageDiffHelper.initImageDiff(diffFileEl, canCreateNote, renderCommentBadge), + ); }; diff --git a/app/assets/javascripts/image_diff/replaced_image_diff.js b/app/assets/javascripts/image_diff/replaced_image_diff.js index 4abd13fb472..8d9e65155d8 100644 --- a/app/assets/javascripts/image_diff/replaced_image_diff.js +++ b/app/assets/javascripts/image_diff/replaced_image_diff.js @@ -26,7 +26,7 @@ export default class ReplacedImageDiff extends ImageDiff { this.imageEls = {}; const viewTypeNames = Object.getOwnPropertyNames(viewTypes); - viewTypeNames.forEach((viewType) => { + viewTypeNames.forEach(viewType => { this.imageEls[viewType] = this.imageFrameEls[viewType].querySelector('img'); }); } @@ -79,13 +79,12 @@ export default class ReplacedImageDiff extends ImageDiff { // Re-render indicator in new view if (indicator.removed) { - const normalizedIndicator = imageDiffHelper - .resizeCoordinatesToImageElement(this.imageEl, { - x: indicator.x, - y: indicator.y, - width: indicator.image.width, - height: indicator.image.height, - }); + const normalizedIndicator = imageDiffHelper.resizeCoordinatesToImageElement(this.imageEl, { + x: indicator.x, + y: indicator.y, + width: indicator.image.width, + height: indicator.image.height, + }); imageDiffHelper.showCommentIndicator(this.imageFrameEl, normalizedIndicator); } } diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js index eda8cdad908..f1beb1a8ea5 100644 --- a/app/assets/javascripts/importer_status.js +++ b/app/assets/javascripts/importer_status.js @@ -60,66 +60,71 @@ class ImporterStatus { attributes = Object.assign(repoData, attributes); } - return axios.post(this.importUrl, attributes) - .then(({ data }) => { - const job = $(`tr#repo_${id}`); - job.attr('id', `project_${data.id}`); - - job.find('.import-target').html(`<a href="${data.full_path}">${data.full_path}</a>`); - $('table.import-jobs tbody').prepend(job); - - job.addClass('table-active'); - const connectingVerb = this.ciCdOnly ? __('connecting') : __('importing'); - job.find('.import-actions').html(sprintf( - _.escape(__('%{loadingIcon} Started')), { - loadingIcon: `<i class="fa fa-spinner fa-spin" aria-label="${_.escape(connectingVerb)}"></i>`, - }, - false, - )); - }) - .catch((error) => { - let details = error; - - const $statusField = $(`#repo_${this.id} .job-status`); - $statusField.text(__('Failed')); - - if (error.response && error.response.data && error.response.data.errors) { - details = error.response.data.errors; - } - - flash(sprintf(__('An error occurred while importing project: %{details}'), { details })); - }); + return axios + .post(this.importUrl, attributes) + .then(({ data }) => { + const job = $(`tr#repo_${id}`); + job.attr('id', `project_${data.id}`); + + job.find('.import-target').html(`<a href="${data.full_path}">${data.full_path}</a>`); + $('table.import-jobs tbody').prepend(job); + + job.addClass('table-active'); + const connectingVerb = this.ciCdOnly ? __('connecting') : __('importing'); + job.find('.import-actions').html( + sprintf( + _.escape(__('%{loadingIcon} Started')), + { + loadingIcon: `<i class="fa fa-spinner fa-spin" aria-label="${_.escape( + connectingVerb, + )}"></i>`, + }, + false, + ), + ); + }) + .catch(error => { + let details = error; + + const $statusField = $(`#repo_${this.id} .job-status`); + $statusField.text(__('Failed')); + + if (error.response && error.response.data && error.response.data.errors) { + details = error.response.data.errors; + } + + flash(sprintf(__('An error occurred while importing project: %{details}'), { details })); + }); } autoUpdate() { - return axios.get(this.jobsUrl) - .then(({ data = [] }) => { - data.forEach((job) => { - const jobItem = $(`#project_${job.id}`); - const statusField = jobItem.find('.job-status'); - - const spinner = '<i class="fa fa-spinner fa-spin"></i>'; - - switch (job.import_status) { - case 'finished': - jobItem.removeClass('table-active').addClass('table-success'); - statusField.html(`<span><i class="fa fa-check"></i> ${__('Done')}</span>`); - break; - case 'scheduled': - statusField.html(`${spinner} ${__('Scheduled')}`); - break; - case 'started': - statusField.html(`${spinner} ${__('Started')}`); - break; - case 'failed': - statusField.html(__('Failed')); - break; - default: - statusField.html(job.import_status); - break; - } - }); + return axios.get(this.jobsUrl).then(({ data = [] }) => { + data.forEach(job => { + const jobItem = $(`#project_${job.id}`); + const statusField = jobItem.find('.job-status'); + + const spinner = '<i class="fa fa-spinner fa-spin"></i>'; + + switch (job.import_status) { + case 'finished': + jobItem.removeClass('table-active').addClass('table-success'); + statusField.html(`<span><i class="fa fa-check"></i> ${__('Done')}</span>`); + break; + case 'scheduled': + statusField.html(`${spinner} ${__('Scheduled')}`); + break; + case 'started': + statusField.html(`${spinner} ${__('Started')}`); + break; + case 'failed': + statusField.html(__('Failed')); + break; + default: + statusField.html(job.import_status); + break; + } }); + }); } setAutoUpdate() { @@ -141,7 +146,4 @@ function initImporterStatus() { } } -export { - initImporterStatus as default, - ImporterStatus, -}; +export { initImporterStatus as default, ImporterStatus }; diff --git a/app/assets/javascripts/init_changes_dropdown.js b/app/assets/javascripts/init_changes_dropdown.js index 5c5a6e01848..e708e5d0978 100644 --- a/app/assets/javascripts/init_changes_dropdown.js +++ b/app/assets/javascripts/init_changes_dropdown.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import { stickyMonitor } from './lib/utils/sticky'; -export default (stickyTop) => { +export default stickyTop => { stickyMonitor(document.querySelector('.js-diff-files-changed'), stickyTop); $('.js-diff-stats-dropdown').glDropdown({ diff --git a/app/assets/javascripts/init_notes.js b/app/assets/javascripts/init_notes.js index 3c71258e53b..a77828e8cf2 100644 --- a/app/assets/javascripts/init_notes.js +++ b/app/assets/javascripts/init_notes.js @@ -2,13 +2,7 @@ import Notes from './notes'; export default () => { const dataEl = document.querySelector('.js-notes-data'); - const { - notesUrl, - notesIds, - now, - diffView, - enableGFM, - } = JSON.parse(dataEl.innerHTML); + const { notesUrl, notesIds, now, diffView, enableGFM } = JSON.parse(dataEl.innerHTML); // Create a singleton so that we don't need to assign // into the window object, we can just access the current isntance with Notes.instance diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js index bd90d0eaa32..08b858305ab 100644 --- a/app/assets/javascripts/integrations/integration_settings_form.js +++ b/app/assets/javascripts/integrations/integration_settings_form.js @@ -97,7 +97,8 @@ export default class IntegrationSettingsForm { testSettings(formData) { this.toggleSubmitBtnState(true); - return axios.put(this.testEndPoint, formData) + return axios + .put(this.testEndPoint, formData) .then(({ data }) => { if (data.error) { let flashActions; @@ -105,7 +106,7 @@ export default class IntegrationSettingsForm { if (data.test_failed) { flashActions = { title: 'Save anyway', - clickHandler: (e) => { + clickHandler: e => { e.preventDefault(); this.$form.submit(); }, diff --git a/app/assets/javascripts/issuable/auto_width_dropdown_select.js b/app/assets/javascripts/issuable/auto_width_dropdown_select.js index 07cf1eff279..612c524ca1c 100644 --- a/app/assets/javascripts/issuable/auto_width_dropdown_select.js +++ b/app/assets/javascripts/issuable/auto_width_dropdown_select.js @@ -27,7 +27,10 @@ class AutoWidthDropdownSelect { // We have to look at the parent because // `offsetParent` on a `display: none;` is `null` - const offsetParentWidth = $(this).parent().offsetParent().width(); + const offsetParentWidth = $(this) + .parent() + .offsetParent() + .width(); // Reset any width to let it naturally flow $dropdown.css('width', 'auto'); if ($dropdown.outerWidth(false) > offsetParentWidth) { diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js index 9848bcc2e64..b844e4c5e5b 100644 --- a/app/assets/javascripts/issuable_bulk_update_actions.js +++ b/app/assets/javascripts/issuable_bulk_update_actions.js @@ -32,7 +32,7 @@ export default { onFormSubmitFailure() { this.form.find('[type="submit"]').enable(); - return new Flash("Issue update failed"); + return new Flash('Issue update failed'); }, getSelectedIssues() { @@ -63,7 +63,7 @@ export default { const result = []; const labelsToKeep = this.$labelDropdown.data('indeterminate'); - this.getLabelsFromSelection().forEach((id) => { + this.getLabelsFromSelection().forEach(id => { if (labelsToKeep.indexOf(id) === -1) { result.push(id); } @@ -89,8 +89,8 @@ export default { issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(), subscription_event: this.form.find('input[name="update[subscription_event]"]').val(), add_label_ids: [], - remove_label_ids: [] - } + remove_label_ids: [], + }, }; if (this.willUpdateLabels) { formData.update.add_label_ids = this.$labelDropdown.data('marked'); @@ -134,7 +134,7 @@ export default { // Collect unique label IDs for all checked issues this.getElement('.selected-issuable:checked').each((i, el) => { issuableLabels = this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'); - issuableLabels.forEach((labelId) => { + issuableLabels.forEach(labelId => { // Store unique IDs if (uniqueIds.indexOf(labelId) === -1) { uniqueIds.push(labelId); diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index 0140960b367..c81a2230310 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -1,6 +1,3 @@ -/* eslint-disable no-new, no-unused-vars, consistent-return, no-else-return */ -/* global GitLab */ - import $ from 'jquery'; import Pikaday from 'pikaday'; import Autosave from './autosave'; @@ -8,7 +5,7 @@ import UsersSelect from './users_select'; import GfmAutoComplete from './gfm_auto_complete'; import ZenMode from './zen_mode'; import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select'; -import { parsePikadayDate, pikadayToString } from './lib/utils/datefix'; +import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility'; export default class IssuableForm { constructor(form) { @@ -19,9 +16,11 @@ export default class IssuableForm { this.handleSubmit = this.handleSubmit.bind(this); this.wipRegex = /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i; - new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup(); - new UsersSelect(); - new ZenMode(); + this.gfmAutoComplete = new GfmAutoComplete( + gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources, + ).setup(); + this.usersSelect = new UsersSelect(); + this.zenMode = new ZenMode(); this.titleField = this.form.find('input[name*="[title]"]'); this.descriptionField = this.form.find('textarea[name*="[description]"]'); @@ -57,8 +56,16 @@ export default class IssuableForm { } initAutosave() { - new Autosave(this.titleField, [document.location.pathname, document.location.search, 'title']); - return new Autosave(this.descriptionField, [document.location.pathname, document.location.search, 'description']); + this.autosave = new Autosave(this.titleField, [ + document.location.pathname, + document.location.search, + 'title', + ]); + return new Autosave(this.descriptionField, [ + document.location.pathname, + document.location.search, + 'description', + ]); } handleSubmit() { @@ -74,7 +81,7 @@ export default class IssuableForm { this.$wipExplanation = this.form.find('.js-wip-explanation'); this.$noWipExplanation = this.form.find('.js-no-wip-explanation'); if (!(this.$wipExplanation.length && this.$noWipExplanation.length)) { - return; + return undefined; } this.form.on('click', '.js-toggle-wip', this.toggleWip); this.titleField.on('keyup blur', this.renderWipExplanation); @@ -89,10 +96,9 @@ export default class IssuableForm { if (this.workInProgress()) { this.$wipExplanation.show(); return this.$noWipExplanation.hide(); - } else { - this.$wipExplanation.hide(); - return this.$noWipExplanation.show(); } + this.$wipExplanation.hide(); + return this.$noWipExplanation.show(); } toggleWip(event) { @@ -110,7 +116,7 @@ export default class IssuableForm { } addWip() { - this.titleField.val(`WIP: ${(this.titleField.val())}`); + this.titleField.val(`WIP: ${this.titleField.val()}`); } initTargetBranchDropdown() { diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue index ba14aaeed2c..3cabbfc6e27 100644 --- a/app/assets/javascripts/jobs/components/job_app.vue +++ b/app/assets/javascripts/jobs/components/job_app.vue @@ -1,164 +1,164 @@ <script> - import _ from 'underscore'; - import { mapGetters, mapState, mapActions } from 'vuex'; - import { isScrolledToBottom } from '~/lib/utils/scroll_utils'; - import bp from '~/breakpoints'; - import CiHeader from '~/vue_shared/components/header_ci_component.vue'; - import Callout from '~/vue_shared/components/callout.vue'; - import createStore from '../store'; - import EmptyState from './empty_state.vue'; - import EnvironmentsBlock from './environments_block.vue'; - import ErasedBlock from './erased_block.vue'; - import Log from './job_log.vue'; - import LogTopBar from './job_log_controllers.vue'; - import StuckBlock from './stuck_block.vue'; - import Sidebar from './sidebar.vue'; +import _ from 'underscore'; +import { mapGetters, mapState, mapActions } from 'vuex'; +import { isScrolledToBottom } from '~/lib/utils/scroll_utils'; +import bp from '~/breakpoints'; +import CiHeader from '~/vue_shared/components/header_ci_component.vue'; +import Callout from '~/vue_shared/components/callout.vue'; +import createStore from '../store'; +import EmptyState from './empty_state.vue'; +import EnvironmentsBlock from './environments_block.vue'; +import ErasedBlock from './erased_block.vue'; +import Log from './job_log.vue'; +import LogTopBar from './job_log_controllers.vue'; +import StuckBlock from './stuck_block.vue'; +import Sidebar from './sidebar.vue'; - export default { - name: 'JobPageApp', - store: createStore(), - components: { - CiHeader, - Callout, - EmptyState, - EnvironmentsBlock, - ErasedBlock, - Log, - LogTopBar, - StuckBlock, - Sidebar, +export default { + name: 'JobPageApp', + store: createStore(), + components: { + CiHeader, + Callout, + EmptyState, + EnvironmentsBlock, + ErasedBlock, + Log, + LogTopBar, + StuckBlock, + Sidebar, + }, + props: { + runnerSettingsUrl: { + type: String, + required: false, + default: null, }, - props: { - runnerSettingsUrl: { - type: String, - required: false, - default: null, - }, - runnerHelpUrl: { - type: String, - required: false, - default: null, - }, - endpoint: { - type: String, - required: true, - }, - terminalPath: { - type: String, - required: false, - default: null, - }, - pagePath: { - type: String, - required: true, - }, - logState: { - type: String, - required: true, - }, + runnerHelpUrl: { + type: String, + required: false, + default: null, }, - computed: { - ...mapState([ - 'isLoading', - 'job', - 'isSidebarOpen', - 'trace', - 'isTraceComplete', - 'traceSize', - 'isTraceSizeVisible', - 'isScrollBottomDisabled', - 'isScrollTopDisabled', - 'isScrolledToBottomBeforeReceivingTrace', - 'hasError', - ]), - ...mapGetters([ - 'headerActions', - 'headerTime', - 'shouldRenderCalloutMessage', - 'shouldRenderTriggeredLabel', - 'hasEnvironment', - 'isJobStuck', - 'hasTrace', - 'emptyStateIllustration', - 'isScrollingDown', - 'emptyStateAction', - ]), + endpoint: { + type: String, + required: true, + }, + terminalPath: { + type: String, + required: false, + default: null, + }, + pagePath: { + type: String, + required: true, + }, + logState: { + type: String, + required: true, + }, + }, + computed: { + ...mapState([ + 'isLoading', + 'job', + 'isSidebarOpen', + 'trace', + 'isTraceComplete', + 'traceSize', + 'isTraceSizeVisible', + 'isScrollBottomDisabled', + 'isScrollTopDisabled', + 'isScrolledToBottomBeforeReceivingTrace', + 'hasError', + ]), + ...mapGetters([ + 'headerActions', + 'headerTime', + 'shouldRenderCalloutMessage', + 'shouldRenderTriggeredLabel', + 'hasEnvironment', + 'hasTrace', + 'emptyStateIllustration', + 'isScrollingDown', + 'emptyStateAction', + 'hasRunnersForProject', + ]), - shouldRenderContent() { - return !this.isLoading && !this.hasError; - } + shouldRenderContent() { + return !this.isLoading && !this.hasError; }, - watch: { - // Once the job log is loaded, - // fetch the stages for the dropdown on the sidebar - job(newVal, oldVal) { - if (_.isEmpty(oldVal) && !_.isEmpty(newVal.pipeline)) { - this.fetchStages(); - } - }, + }, + watch: { + // Once the job log is loaded, + // fetch the stages for the dropdown on the sidebar + job(newVal, oldVal) { + if (_.isEmpty(oldVal) && !_.isEmpty(newVal.pipeline)) { + this.fetchStages(); + } }, - created() { - this.throttled = _.throttle(this.toggleScrollButtons, 100); + }, + created() { + this.throttled = _.throttle(this.toggleScrollButtons, 100); - this.setJobEndpoint(this.endpoint); - this.setTraceOptions({ - logState: this.logState, - pagePath: this.pagePath, - }); + this.setJobEndpoint(this.endpoint); + this.setTraceOptions({ + logState: this.logState, + pagePath: this.pagePath, + }); - this.fetchJob(); - this.fetchTrace(); + this.fetchJob(); + this.fetchTrace(); - window.addEventListener('resize', this.onResize); - window.addEventListener('scroll', this.updateScroll); - }, + window.addEventListener('resize', this.onResize); + window.addEventListener('scroll', this.updateScroll); + }, - mounted() { + mounted() { + this.updateSidebar(); + }, + + destroyed() { + window.removeEventListener('resize', this.onResize); + window.removeEventListener('scroll', this.updateScroll); + }, + + methods: { + ...mapActions([ + 'setJobEndpoint', + 'setTraceOptions', + 'fetchJob', + 'fetchStages', + 'hideSidebar', + 'showSidebar', + 'toggleSidebar', + 'fetchTrace', + 'scrollBottom', + 'scrollTop', + 'toggleScrollButtons', + 'toggleScrollAnimation', + ]), + onResize() { this.updateSidebar(); + this.updateScroll(); }, - - destroyed() { - window.removeEventListener('resize', this.onResize); - window.removeEventListener('scroll', this.updateScroll); + updateSidebar() { + if (bp.getBreakpointSize() === 'xs') { + this.hideSidebar(); + } else if (!this.isSidebarOpen) { + this.showSidebar(); + } }, + updateScroll() { + if (!isScrolledToBottom()) { + this.toggleScrollAnimation(false); + } else if (this.isScrollingDown) { + this.toggleScrollAnimation(true); + } - methods: { - ...mapActions([ - 'setJobEndpoint', - 'setTraceOptions', - 'fetchJob', - 'fetchStages', - 'hideSidebar', - 'showSidebar', - 'toggleSidebar', - 'fetchTrace', - 'scrollBottom', - 'scrollTop', - 'toggleScrollButtons', - 'toggleScrollAnimation', - ]), - onResize() { - this.updateSidebar(); - this.updateScroll(); - }, - updateSidebar() { - if (bp.getBreakpointSize() === 'xs') { - this.hideSidebar(); - } else if (!this.isSidebarOpen) { - this.showSidebar(); - } - }, - updateScroll() { - if (!isScrolledToBottom()) { - this.toggleScrollAnimation(false); - } else if (this.isScrollingDown) { - this.toggleScrollAnimation(true); - } - - this.throttled(); - }, + this.throttled(); }, - }; + }, +}; </script> <template> <div> @@ -195,9 +195,9 @@ <!-- Body Section --> <stuck-block - v-if="isJobStuck" + v-if="job.stuck" class="js-job-stuck" - :has-no-runners-for-project="job.runners.available" + :has-no-runners-for-project="hasRunnersForProject" :tags="job.tags" :runners-path="runnerSettingsUrl" /> diff --git a/app/assets/javascripts/jobs/components/job_log.vue b/app/assets/javascripts/jobs/components/job_log.vue index ffa6ada3e28..92e20e92d66 100644 --- a/app/assets/javascripts/jobs/components/job_log.vue +++ b/app/assets/javascripts/jobs/components/job_log.vue @@ -1,45 +1,45 @@ <script> - import { mapState, mapActions } from 'vuex'; +import { mapState, mapActions } from 'vuex'; - export default { - name: 'JobLog', - props: { - trace: { - type: String, - required: true, - }, - isComplete: { - type: Boolean, - required: true, - }, +export default { + name: 'JobLog', + props: { + trace: { + type: String, + required: true, }, - computed: { - ...mapState(['isScrolledToBottomBeforeReceivingTrace']), + isComplete: { + type: Boolean, + required: true, }, - updated() { - this.$nextTick(() => this.handleScrollDown()); + }, + computed: { + ...mapState(['isScrolledToBottomBeforeReceivingTrace']), + }, + updated() { + this.$nextTick(() => this.handleScrollDown()); + }, + mounted() { + this.$nextTick(() => this.handleScrollDown()); + }, + methods: { + ...mapActions(['scrollBottom']), + /** + * The job log is sent in HTML, which means we need to use `v-html` to render it + * Using the updated hook with $nextTick is not enough to wait for the DOM to be updated + * in this case because it runs before `v-html` has finished running, since there's no + * Vue binding. + * In order to scroll the page down after `v-html` has finished, we need to use setTimeout + */ + handleScrollDown() { + if (this.isScrolledToBottomBeforeReceivingTrace) { + setTimeout(() => { + this.scrollBottom(); + }, 0); + } }, - mounted() { - this.$nextTick(() => this.handleScrollDown()); - }, - methods: { - ...mapActions(['scrollBottom']), - /** - * The job log is sent in HTML, which means we need to use `v-html` to render it - * Using the updated hook with $nextTick is not enough to wait for the DOM to be updated - * in this case because it runs before `v-html` has finished running, since there's no - * Vue binding. - * In order to scroll the page down after `v-html` has finished, we need to use setTimeout - */ - handleScrollDown() { - if (this.isScrolledToBottomBeforeReceivingTrace) { - setTimeout(() => { - this.scrollBottom(); - }, 0); - } - }, - }, - }; + }, +}; </script> <template> <pre class="js-build-trace build-trace qa-build-trace"> diff --git a/app/assets/javascripts/jobs/components/sidebar.vue b/app/assets/javascripts/jobs/components/sidebar.vue index 906769ee6a2..28a02230d89 100644 --- a/app/assets/javascripts/jobs/components/sidebar.vue +++ b/app/assets/javascripts/jobs/components/sidebar.vue @@ -31,7 +31,7 @@ export default { }, }, computed: { - ...mapState(['job', 'stages', 'jobs', 'selectedStage']), + ...mapState(['job', 'stages', 'jobs', 'selectedStage', 'isLoadingStages']), coverage() { return `${this.job.coverage}%`; }, @@ -59,10 +59,10 @@ export default { return ''; } - let t = this.job.metadata.timeout_human_readable; - if (this.job.metadata.timeout_source !== '') { - t += ` (from ${this.job.metadata.timeout_source})`; - } + let t = this.job.metadata.timeout_human_readable; + if (this.job.metadata.timeout_source !== '') { + t += ` (from ${this.job.metadata.timeout_source})`; + } return t; }, @@ -270,6 +270,7 @@ export default { /> <stages-dropdown + v-if="!isLoadingStages" :stages="stages" :pipeline="job.pipeline" :selected-stage="selectedStage" diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue index e5e1d56e287..dc26b246d71 100644 --- a/app/assets/javascripts/jobs/components/stages_dropdown.vue +++ b/app/assets/javascripts/jobs/components/stages_dropdown.vue @@ -22,7 +22,6 @@ export default { required: true, }, }, - computed: { hasRef() { return !_.isEmpty(this.pipeline.ref); diff --git a/app/assets/javascripts/jobs/components/stuck_block.vue b/app/assets/javascripts/jobs/components/stuck_block.vue index a60643b2c65..1d5789b175a 100644 --- a/app/assets/javascripts/jobs/components/stuck_block.vue +++ b/app/assets/javascripts/jobs/components/stuck_block.vue @@ -23,14 +23,7 @@ export default { <template> <div class="bs-callout bs-callout-warning"> <p - v-if="hasNoRunnersForProject" - class="js-stuck-no-runners append-bottom-0" - > - {{ s__(`Job|This job is stuck, because the project - doesn't have any runners online assigned to it.`) }} - </p> - <p - v-else-if="tags.length" + v-if="tags.length" class="js-stuck-with-tags append-bottom-0" > {{ s__(`This job is stuck, because you don't have @@ -44,6 +37,13 @@ export default { </span> </p> <p + v-else-if="hasNoRunnersForProject" + class="js-stuck-no-runners append-bottom-0" + > + {{ s__(`Job|This job is stuck, because the project + doesn't have any runners online assigned to it.`) }} + </p> + <p v-else class="js-stuck-no-active-runner append-bottom-0" > diff --git a/app/assets/javascripts/jobs/index.js b/app/assets/javascripts/jobs/index.js index ccd096a1da5..a32e945627c 100644 --- a/app/assets/javascripts/jobs/index.js +++ b/app/assets/javascripts/jobs/index.js @@ -23,4 +23,3 @@ export default () => { }, }); }; - diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js index 4ce395a9106..d440b2c9ef1 100644 --- a/app/assets/javascripts/jobs/store/getters.js +++ b/app/assets/javascripts/jobs/store/getters.js @@ -35,23 +35,19 @@ export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status); * Used to check if it should render the job log or the empty state * @returns {Boolean} */ -export const hasTrace = state => state.job.has_trace || (!_.isEmpty(state.job.status) && state.job.status.group === 'running'); +export const hasTrace = state => + state.job.has_trace || (!_.isEmpty(state.job.status) && state.job.status.group === 'running'); export const emptyStateIllustration = state => (state.job && state.job.status && state.job.status.illustration) || {}; -export const emptyStateAction = state => (state.job && state.job.status && state.job.status.action) || {}; -/** - * When the job is pending and there are no available runners - * we need to render the stuck block; - * - * @returns {Boolean} - */ -export const isJobStuck = state => - (!_.isEmpty(state.job.status) && state.job.status.group === 'pending') && - (!_.isEmpty(state.job.runners) && state.job.runners.available === false); +export const emptyStateAction = state => + (state.job && state.job.status && state.job.status.action) || {}; export const isScrollingDown = state => isScrolledToBottom() && !state.isTraceComplete; +export const hasRunnersForProject = state => + state.job.runners.available && !state.job.runners.online; + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js index 4195d787f12..cd440d21c1f 100644 --- a/app/assets/javascripts/jobs/store/mutations.js +++ b/app/assets/javascripts/jobs/store/mutations.js @@ -71,7 +71,7 @@ export default { * after the first request, * and we do not want to hijack that */ - if (state.selectedStage === 'More' && job.stage) { + if (state.selectedStage === '' && job.stage) { state.selectedStage = job.stage; } }, diff --git a/app/assets/javascripts/jobs/store/state.js b/app/assets/javascripts/jobs/store/state.js index 0eb269ca38f..04825187c99 100644 --- a/app/assets/javascripts/jobs/store/state.js +++ b/app/assets/javascripts/jobs/store/state.js @@ -1,5 +1,3 @@ -import { __ } from '~/locale'; - export default () => ({ jobEndpoint: null, traceEndpoint: null, @@ -29,7 +27,7 @@ export default () => ({ // sidebar dropdown & list of jobs isLoadingStages: false, isLoadingJobs: false, - selectedStage: __('More'), + selectedStage: '', stages: [], jobs: [], }); diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 3c38d998b6c..5457604b3b9 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -25,7 +25,35 @@ export default class LabelsSelect { } $els.each(function(i, dropdown) { - var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer; + var $block, + $colorPreview, + $dropdown, + $form, + $loading, + $selectbox, + $sidebarCollapsedValue, + $value, + abilityName, + defaultLabel, + enableLabelCreateButton, + issueURLSplit, + issueUpdateURL, + labelUrl, + namespacePath, + projectPath, + saveLabelData, + selectedLabel, + showAny, + showNo, + $sidebarLabelTooltip, + initialSelected, + $toggleText, + fieldName, + useId, + propertyName, + showMenuAbove, + $container, + $dropdownContainer; $dropdown = $(dropdown); $dropdownContainer = $dropdown.closest('.labels-filter'); $toggleText = $dropdown.find('.dropdown-toggle-text'); @@ -34,7 +62,7 @@ export default class LabelsSelect { labelUrl = $dropdown.data('labels'); issueUpdateURL = $dropdown.data('issueUpdate'); selectedLabel = $dropdown.data('selected'); - if ((selectedLabel != null) && !$dropdown.hasClass('js-multiselect')) { + if (selectedLabel != null && !$dropdown.hasClass('js-multiselect')) { selectedLabel = selectedLabel.split(','); } showNo = $dropdown.data('showNo'); @@ -50,26 +78,37 @@ export default class LabelsSelect { $value = $block.find('.value'); $loading = $block.find('.block-loading').fadeOut(); fieldName = $dropdown.data('fieldName'); - useId = $dropdown.is('.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown'); + useId = $dropdown.is( + '.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown', + ); propertyName = useId ? 'id' : 'title'; initialSelected = $selectbox .find('input[name="' + $dropdown.data('fieldName') + '"]') - .map(function () { + .map(function() { return this.value; - }).get(); + }) + .get(); const { handleClick } = options; $sidebarLabelTooltip.tooltip(); if ($dropdown.closest('.dropdown').find('.dropdown-new-label').length) { - new CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), namespacePath, projectPath); + new CreateLabelDropdown( + $dropdown.closest('.dropdown').find('.dropdown-new-label'), + namespacePath, + projectPath, + ); } saveLabelData = function() { var data, selected; - selected = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "']").map(function() { - return this.value; - }).get(); + selected = $dropdown + .closest('.selectbox') + .find("input[name='" + fieldName + "']") + .map(function() { + return this.value; + }) + .get(); if (_.isEqual(initialSelected, selected)) return; initialSelected = selected; @@ -82,7 +121,8 @@ export default class LabelsSelect { } $loading.removeClass('hidden').fadeIn(); $dropdown.trigger('loading.gl.dropdown'); - axios.put(issueUpdateURL, data) + axios + .put(issueUpdateURL, data) .then(({ data }) => { var labelCount, template, labelTooltipTitle, labelTitles, formattedLabels; $loading.fadeOut(); @@ -96,8 +136,7 @@ export default class LabelsSelect { issueUpdateURL, }); labelCount = data.labels.length; - } - else { + } else { template = '<span class="no-value">None</span>'; } $value.removeAttr('style').html(template); @@ -114,17 +153,14 @@ export default class LabelsSelect { } labelTooltipTitle = labelTitles.join(', '); - } - else { + } else { labelTooltipTitle = __('Labels'); } - $sidebarLabelTooltip - .attr('title', labelTooltipTitle) - .tooltip('_fixTitle'); + $sidebarLabelTooltip.attr('title', labelTooltipTitle).tooltip('_fixTitle'); $('.has-tooltip', $value).tooltip({ - container: 'body' + container: 'body', }); }) .catch(() => flash(__('Error saving label update.'))); @@ -132,34 +168,38 @@ export default class LabelsSelect { $dropdown.glDropdown({ showMenuAbove: showMenuAbove, data: function(term, callback) { - axios.get(labelUrl) - .then((res) => { - let data = _.chain(res.data).groupBy(function(label) { - return label.title; - }).map(function(label) { - var color; - color = _.map(label, function(dup) { - return dup.color; - }); - return { - id: label[0].id, - title: label[0].title, - color: color, - duplicate: color.length > 1 - }; - }).value(); + axios + .get(labelUrl) + .then(res => { + let data = _.chain(res.data) + .groupBy(function(label) { + return label.title; + }) + .map(function(label) { + var color; + color = _.map(label, function(dup) { + return dup.color; + }); + return { + id: label[0].id, + title: label[0].title, + color: color, + duplicate: color.length > 1, + }; + }) + .value(); if ($dropdown.hasClass('js-extra-options')) { var extraData = []; if (showNo) { extraData.unshift({ id: 0, - title: 'No Label' + title: 'No Label', }); } if (showAny) { extraData.unshift({ isAny: true, - title: 'Any Label' + title: 'Any Label', }); } if (extraData.length) { @@ -176,11 +216,22 @@ export default class LabelsSelect { .catch(() => flash(__('Error fetching labels.'))); }, renderRow: function(label, instance) { - var $a, $li, color, colorEl, indeterminate, removesAll, selectedClass, spacing, i, marked, dropdownName, dropdownValue; + var $a, + $li, + color, + colorEl, + indeterminate, + removesAll, + selectedClass, + spacing, + i, + marked, + dropdownName, + dropdownValue; $li = $('<li>'); $a = $('<a href="#">'); selectedClass = []; - removesAll = label.id <= 0 || (label.id == null); + removesAll = label.id <= 0 || label.id == null; if ($dropdown.hasClass('js-filter-bulk-update')) { indeterminate = $dropdown.data('indeterminate') || []; marked = $dropdown.data('marked') || []; @@ -200,9 +251,19 @@ export default class LabelsSelect { } else { if (this.id(label)) { dropdownName = $dropdown.data('fieldName'); - dropdownValue = this.id(label).toString().replace(/'/g, '\\\''); - - if ($form.find("input[type='hidden'][name='" + dropdownName + "'][value='" + dropdownValue + "']").length) { + dropdownValue = this.id(label) + .toString() + .replace(/'/g, "\\'"); + + if ( + $form.find( + "input[type='hidden'][name='" + + dropdownName + + "'][value='" + + dropdownValue + + "']", + ).length + ) { selectedClass.push('is-active'); } } @@ -213,16 +274,14 @@ export default class LabelsSelect { } if (label.duplicate) { color = DropdownUtils.duplicateLabelColor(label.color); - } - else { + } else { if (label.color != null) { [color] = label.color; } } if (color) { colorEl = "<span class='dropdown-label-box' style='background: " + color + "'></span>"; - } - else { + } else { colorEl = ''; } // We need to identify which items are actually labels @@ -235,7 +294,7 @@ export default class LabelsSelect { return $li.html($a).prop('outerHTML'); }, search: { - fields: ['title'] + fields: ['title'], }, selectable: true, filterable: true, @@ -255,25 +314,21 @@ export default class LabelsSelect { if (selected && selected.id === 0) { this.selected = []; return 'No Label'; - } - else if (isSelected) { + } else if (isSelected) { this.selected.push(title); - } - else if (!isSelected && title) { + } else if (!isSelected && title) { var index = this.selected.indexOf(title); this.selected.splice(index, 1); } if (selectedLabels.length === 1) { return selectedLabels; - } - else if (selectedLabels.length) { + } else if (selectedLabels.length) { return sprintf(__('%{firstLabel} +%{labelCount} more'), { firstLabel: selectedLabels[0], - labelCount: selectedLabels.length - 1 + labelCount: selectedLabels.length - 1, }); - } - else { + } else { return defaultLabel; } }, @@ -285,10 +340,9 @@ export default class LabelsSelect { return label.id; } - if ($dropdown.hasClass("js-filter-submit") && (label.isAny == null)) { + if ($dropdown.hasClass('js-filter-submit') && label.isAny == null) { return label.title; - } - else { + } else { return label.id; } }, @@ -310,13 +364,13 @@ export default class LabelsSelect { } if ($dropdown.hasClass('js-multiselect')) { if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { - selectedLabels = $dropdown.closest('form').find("input:hidden[name='" + ($dropdown.data('fieldName')) + "']"); + selectedLabels = $dropdown + .closest('form') + .find("input:hidden[name='" + $dropdown.data('fieldName') + "']"); Issuable.filterResults($dropdown.closest('form')); - } - else if ($dropdown.hasClass('js-filter-submit')) { + } else if ($dropdown.hasClass('js-filter-submit')) { $dropdown.closest('form').submit(); - } - else { + } else { if (!$dropdown.hasClass('js-filter-bulk-update')) { saveLabelData(); } @@ -325,7 +379,7 @@ export default class LabelsSelect { }, multiSelect: $dropdown.hasClass('js-multiselect'), vue: $dropdown.hasClass('js-issue-board-sidebar'), - clicked: function (clickEvent) { + clicked: function(clickEvent) { const { $el, e, isMarking } = clickEvent; const label = clickEvent.selectedObj; @@ -339,7 +393,8 @@ export default class LabelsSelect { isMRIndex = page === 'projects:merge_requests:index'; if ($dropdown.parent().find('.is-active:not(.dropdown-clear-active)').length) { - $dropdown.parent() + $dropdown + .parent() .find('.dropdown-clear-active') .removeClass('is-active'); } @@ -367,28 +422,26 @@ export default class LabelsSelect { e.preventDefault(); return; - } - else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { + } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { if (!$dropdown.hasClass('js-multiselect')) { selectedLabel = label.title; return Issuable.filterResults($dropdown.closest('form')); } - } - else if ($dropdown.hasClass('js-filter-submit')) { + } else if ($dropdown.hasClass('js-filter-submit')) { return $dropdown.closest('form').submit(); - } - else if ($dropdown.hasClass('js-issue-board-sidebar')) { + } else if ($dropdown.hasClass('js-issue-board-sidebar')) { if ($el.hasClass('is-active')) { - boardsStore.detail.issue.labels.push(new ListLabel({ - id: label.id, - title: label.title, - color: label.color[0], - textColor: '#fff' - })); - } - else { + boardsStore.detail.issue.labels.push( + new ListLabel({ + id: label.id, + title: label.title, + color: label.color[0], + textColor: '#fff', + }), + ); + } else { var { labels } = boardsStore.detail.issue; - labels = labels.filter(function (selectedLabel) { + labels = labels.filter(function(selectedLabel) { return selectedLabel.id !== label.id; }); boardsStore.detail.issue.labels = labels; @@ -396,19 +449,16 @@ export default class LabelsSelect { $loading.fadeIn(); - boardsStore.detail.issue.update($dropdown.attr('data-issue-update')) + boardsStore.detail.issue + .update($dropdown.attr('data-issue-update')) .then(fadeOutLoader) .catch(fadeOutLoader); - } - else if (handleClick) { + } else if (handleClick) { e.preventDefault(); handleClick(label); - } - else { + } else { if ($dropdown.hasClass('js-multiselect')) { - - } - else { + } else { return saveLabelData(); } } @@ -436,15 +486,17 @@ export default class LabelsSelect { // so best approach is to use traditional way of // concatenation // see: http://2ality.com/2016/05/template-literal-whitespace.html#joining-arrays - const tpl = _.template([ - '<% _.each(labels, function(label){ %>', - '<a href="<%- issueUpdateURL.slice(0, issueUpdateURL.lastIndexOf("/")) %>?label_name[]=<%- encodeURIComponent(label.title) %>">', - '<span class="badge label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">', - '<%- label.title %>', - '</span>', - '</a>', - '<% }); %>', - ].join('')); + const tpl = _.template( + [ + '<% _.each(labels, function(label){ %>', + '<a href="<%- issueUpdateURL.slice(0, issueUpdateURL.lastIndexOf("/")) %>?label_name[]=<%- encodeURIComponent(label.title) %>">', + '<span class="badge label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">', + '<%- label.title %>', + '</span>', + '</a>', + '<% }); %>', + ].join(''), + ); return tpl(tplData); } diff --git a/app/assets/javascripts/lib/utils/ace_utils.js b/app/assets/javascripts/lib/utils/ace_utils.js index efc4b2a8d94..ee71ae0e61a 100644 --- a/app/assets/javascripts/lib/utils/ace_utils.js +++ b/app/assets/javascripts/lib/utils/ace_utils.js @@ -1,6 +1,6 @@ /* global ace */ export default function getModeByFileExtension(path) { - const modelist = ace.require("ace/ext/modelist"); + const modelist = ace.require('ace/ext/modelist'); return modelist.getModeForPath(path).mode; -}; +} diff --git a/app/assets/javascripts/lib/utils/datefix.js b/app/assets/javascripts/lib/utils/datefix.js deleted file mode 100644 index 19e4085dbbb..00000000000 --- a/app/assets/javascripts/lib/utils/datefix.js +++ /dev/null @@ -1,28 +0,0 @@ -export const pad = (val, len = 2) => `0${val}`.slice(-len); - -/** - * Formats dates in Pickaday - * @param {String} dateString Date in yyyy-mm-dd format - * @return {Date} UTC format - */ -export const parsePikadayDate = dateString => { - const parts = dateString.split('-'); - const year = parseInt(parts[0], 10); - const month = parseInt(parts[1] - 1, 10); - const day = parseInt(parts[2], 10); - - return new Date(year, month, day); -}; - -/** - * Used `onSelect` method in pickaday - * @param {Date} date UTC format - * @return {String} Date formated in yyyy-mm-dd - */ -export const pikadayToString = date => { - const day = pad(date.getDate()); - const month = pad(date.getMonth() + 1); - const year = date.getFullYear(); - - return `${year}-${month}-${day}`; -}; diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 833dbefd3dc..46740308f17 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import _ from 'underscore'; import timeago from 'timeago.js'; import dateFormat from 'dateformat'; import { pluralize } from './text_utility'; @@ -46,6 +47,8 @@ const getMonthNames = abbreviated => { ]; }; +export const pad = (val, len = 2) => `0${val}`.slice(-len); + /** * Given a date object returns the day of the week in English * @param {date} date @@ -74,10 +77,10 @@ let timeagoInstance; /** * Sets a timeago Instance */ -export function getTimeago() { +export const getTimeago = () => { if (!timeagoInstance) { - const localeRemaining = function getLocaleRemaining(number, index) { - return [ + const localeRemaining = (number, index) => + [ [s__('Timeago|just now'), s__('Timeago|right now')], [s__('Timeago|%s seconds ago'), s__('Timeago|%s seconds remaining')], [s__('Timeago|1 minute ago'), s__('Timeago|1 minute remaining')], @@ -93,9 +96,9 @@ export function getTimeago() { [s__('Timeago|1 year ago'), s__('Timeago|1 year remaining')], [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')], ][index]; - }; - const locale = function getLocale(number, index) { - return [ + + const locale = (number, index) => + [ [s__('Timeago|just now'), s__('Timeago|right now')], [s__('Timeago|%s seconds ago'), s__('Timeago|in %s seconds')], [s__('Timeago|1 minute ago'), s__('Timeago|in 1 minute')], @@ -111,7 +114,6 @@ export function getTimeago() { [s__('Timeago|1 year ago'), s__('Timeago|in 1 year')], [s__('Timeago|%s years ago'), s__('Timeago|in %s years')], ][index]; - }; timeago.register(timeagoLanguageCode, locale); timeago.register(`${timeagoLanguageCode}-remaining`, localeRemaining); @@ -119,7 +121,7 @@ export function getTimeago() { } return timeagoInstance; -} +}; /** * For the given element, renders a timeago instance. @@ -184,7 +186,7 @@ export const getDayDifference = (a, b) => { * @param {Number} seconds * @return {String} */ -export function timeIntervalInWords(intervalInSeconds) { +export const timeIntervalInWords = intervalInSeconds => { const secondsInteger = parseInt(intervalInSeconds, 10); const minutes = Math.floor(secondsInteger / 60); const seconds = secondsInteger - minutes * 60; @@ -196,9 +198,9 @@ export function timeIntervalInWords(intervalInSeconds) { text = `${seconds} ${pluralize('second', seconds)}`; } return text; -} +}; -export function dateInWords(date, abbreviated = false, hideYear = false) { +export const dateInWords = (date, abbreviated = false, hideYear = false) => { if (!date) return date; const month = date.getMonth(); @@ -240,7 +242,7 @@ export function dateInWords(date, abbreviated = false, hideYear = false) { } return `${monthName} ${date.getDate()}, ${year}`; -} +}; /** * Returns month name based on provided date. @@ -391,3 +393,95 @@ export const formatTime = milliseconds => { formattedTime += remainingSeconds; return formattedTime; }; + +/** + * Formats dates in Pickaday + * @param {String} dateString Date in yyyy-mm-dd format + * @return {Date} UTC format + */ +export const parsePikadayDate = dateString => { + const parts = dateString.split('-'); + const year = parseInt(parts[0], 10); + const month = parseInt(parts[1] - 1, 10); + const day = parseInt(parts[2], 10); + + return new Date(year, month, day); +}; + +/** + * Used `onSelect` method in pickaday + * @param {Date} date UTC format + * @return {String} Date formated in yyyy-mm-dd + */ +export const pikadayToString = date => { + const day = pad(date.getDate()); + const month = pad(date.getMonth() + 1); + const year = date.getFullYear(); + + return `${year}-${month}-${day}`; +}; + +/** + * Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # } + * Seconds can be negative or positive, zero or non-zero. Can be configured for any day + * or week length. + */ +export const parseSeconds = (seconds, { daysPerWeek = 5, hoursPerDay = 8 } = {}) => { + const DAYS_PER_WEEK = daysPerWeek; + const HOURS_PER_DAY = hoursPerDay; + const MINUTES_PER_HOUR = 60; + const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR; + const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR; + + const timePeriodConstraints = { + weeks: MINUTES_PER_WEEK, + days: MINUTES_PER_DAY, + hours: MINUTES_PER_HOUR, + minutes: 1, + }; + + let unorderedMinutes = Math.abs(seconds / MINUTES_PER_HOUR); + + return _.mapObject(timePeriodConstraints, minutesPerPeriod => { + const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod); + + unorderedMinutes -= periodCount * minutesPerPeriod; + + return periodCount; + }); +}; + +/** + * Accepts a timeObject (see parseSeconds) and returns a condensed string representation of it + * (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included. + */ +export const stringifyTime = timeObject => { + const reducedTime = _.reduce( + timeObject, + (memo, unitValue, unitName) => { + const isNonZero = !!unitValue; + return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo; + }, + '', + ).trim(); + return reducedTime.length ? reducedTime : '0m'; +}; + +/** + * Accepts a time string of any size (e.g. '1w 2d 3h 5m' or '1w 2d') and returns + * the first non-zero unit/value pair. + */ +export const abbreviateTime = timeStr => + timeStr.split(' ').filter(unitStr => unitStr.charAt(0) !== '0')[0]; + +/** + * Calculates the milliseconds between now and a given date string. + * The result cannot become negative. + * + * @param endDate date string that the time difference is calculated for + * @return {number} number of milliseconds remaining until the given date + */ +export const calculateRemainingMilliseconds = endDate => { + const remainingMilliseconds = new Date(endDate).getTime() - Date.now(); + return Math.max(remainingMilliseconds, 0); +}; diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js index afbab59055b..2ccc51c35f7 100644 --- a/app/assets/javascripts/lib/utils/number_utils.js +++ b/app/assets/javascripts/lib/utils/number_utils.js @@ -7,7 +7,7 @@ import { BYTES_IN_KIB } from './constants'; * * * Show 3 digits to the right * * For 2 digits to the left of the decimal point and X digits to the right of it * * * Show 2 digits to the right -*/ + */ export function formatRelevantDigits(number) { let digitsLeft = ''; let relevantDigits = 0; diff --git a/app/assets/javascripts/lib/utils/pretty_time.js b/app/assets/javascripts/lib/utils/pretty_time.js deleted file mode 100644 index d92b8a7179f..00000000000 --- a/app/assets/javascripts/lib/utils/pretty_time.js +++ /dev/null @@ -1,63 +0,0 @@ -import _ from 'underscore'; - -/* - * TODO: Make these methods more configurable (e.g. stringifyTime condensed or - * non-condensed, abbreviateTimelengths) - * */ - -/* - * Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # } - * Seconds can be negative or positive, zero or non-zero. Can be configured for any day - * or week length. -*/ - -export function parseSeconds(seconds, { daysPerWeek = 5, hoursPerDay = 8 } = {}) { - const DAYS_PER_WEEK = daysPerWeek; - const HOURS_PER_DAY = hoursPerDay; - const MINUTES_PER_HOUR = 60; - const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR; - const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR; - - const timePeriodConstraints = { - weeks: MINUTES_PER_WEEK, - days: MINUTES_PER_DAY, - hours: MINUTES_PER_HOUR, - minutes: 1, - }; - - let unorderedMinutes = Math.abs(seconds / MINUTES_PER_HOUR); - - return _.mapObject(timePeriodConstraints, minutesPerPeriod => { - const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod); - - unorderedMinutes -= periodCount * minutesPerPeriod; - - return periodCount; - }); -} - -/* -* Accepts a timeObject (see parseSeconds) and returns a condensed string representation of it -* (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included. -*/ - -export function stringifyTime(timeObject) { - const reducedTime = _.reduce( - timeObject, - (memo, unitValue, unitName) => { - const isNonZero = !!unitValue; - return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo; - }, - '', - ).trim(); - return reducedTime.length ? reducedTime : '0m'; -} - -/* -* Accepts a time string of any size (e.g. '1w 2d 3h 5m' or '1w 2d') and returns -* the first non-zero unit/value pair. -*/ - -export function abbreviateTime(timeStr) { - return timeStr.split(' ').filter(unitStr => unitStr.charAt(0) !== '0')[0]; -} diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js index e26a6b986be..c52cfb806a2 100644 --- a/app/assets/javascripts/lib/utils/text_markdown.js +++ b/app/assets/javascripts/lib/utils/text_markdown.js @@ -2,6 +2,8 @@ import $ from 'jquery'; import { insertText } from '~/lib/utils/common_utils'; +const LINK_TAG_PATTERN = '[{text}](url)'; + function selectedText(text, textarea) { return text.substring(textarea.selectionStart, textarea.selectionEnd); } @@ -76,6 +78,21 @@ export function insertMarkdownText({ textArea, text, tag, blockTag, selected, wr removedFirstNewLine = false; currentLineEmpty = false; + // check for link pattern and selected text is an URL + // if so fill in the url part instead of the text part of the pattern. + if (tag === LINK_TAG_PATTERN) { + if (URL) { + try { + const ignoredUrl = new URL(selected); + // valid url + tag = '[text]({text})'; + select = 'text'; + } catch (e) { + // ignore - no valid url + } + } + } + // Remove the first newline if (selected.indexOf('\n') === 0) { removedFirstNewLine = true; diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js index df5cd1b8c51..0beedcacf33 100644 --- a/app/assets/javascripts/member_expiration_date.js +++ b/app/assets/javascripts/member_expiration_date.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import Pikaday from 'pikaday'; -import { parsePikadayDate, pikadayToString } from './lib/utils/datefix'; +import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility'; // Add datepickers to all `js-access-expiration-date` elements. If those elements are // children of an element with the `clearable-input` class, and have a sibling diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js index 7d0c701fd70..bd263c75a3d 100644 --- a/app/assets/javascripts/members.js +++ b/app/assets/javascripts/members.js @@ -7,8 +7,12 @@ export default class Members { } addListeners() { - $('.js-member-update-control').off('change').on('change', this.formSubmit.bind(this)); - $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess.bind(this)); + $('.js-member-update-control') + .off('change') + .on('change', this.formSubmit.bind(this)); + $('.js-edit-member-form') + .off('ajax:success') + .on('ajax:success', this.formSuccess.bind(this)); gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); } @@ -28,7 +32,7 @@ export default class Members { toggleLabel(selected, $el) { return $el.text(); }, - clicked: (options) => { + clicked: options => { this.formSubmit(null, options.$el); }, }); diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 42fb5c7177a..d32f39881dd 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -9,7 +9,10 @@ import '~/gl_dropdown'; import axios from './lib/utils/axios_utils'; import { timeFor } from './lib/utils/datetime_utility'; import ModalStore from './boards/stores/modal_store'; -import boardsStore, { boardStoreIssueSet, boardStoreIssueDelete } from './boards/stores/boards_store'; +import boardsStore, { + boardStoreIssueSet, + boardStoreIssueDelete, +} from './boards/stores/boards_store'; export default class MilestoneSelect { constructor(currentProject, els, options = {}) { diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 67338aa96c3..98182d92c2f 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -149,7 +149,7 @@ export default { .catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))), this.service .getEnvironmentsData() - .then((data) => this.store.storeEnvironmentsData(data)) + .then(data => this.store.storeEnvironmentsData(data)) .catch(() => Flash(s__('Metrics|There was an error getting environments information.'))), ]) .then(() => { @@ -157,6 +157,7 @@ export default { this.state = 'noData'; return; } + this.showEmptyState = false; }) .then(this.resize) @@ -195,7 +196,10 @@ export default { name="chevron-down" /> </button> - <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up"> + <div + v-if="store.environmentsData.length > 0" + class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up" + > <ul> <li v-for="environment in store.environmentsData" diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue index ed5c8b15945..5c6e2e09e46 100644 --- a/app/assets/javascripts/monitoring/components/graph.vue +++ b/app/assets/javascripts/monitoring/components/graph.vue @@ -121,6 +121,7 @@ export default { draw() { const breakpointSize = bp.getBreakpointSize(); const query = this.graphData.queries[0]; + const svgWidth = this.$refs.baseSvg.getBoundingClientRect().width; this.margin = measurements.large.margin; if (this.smallGraph || breakpointSize === 'xs' || breakpointSize === 'sm') { this.graphHeight = 300; @@ -130,13 +131,13 @@ export default { this.unitOfDisplay = query.unit || ''; this.yAxisLabel = this.graphData.y_label || 'Values'; this.legendTitle = query.label || 'Average'; - this.graphWidth = this.$refs.baseSvg.clientWidth - this.margin.left - this.margin.right; + this.graphWidth = svgWidth - this.margin.left - this.margin.right; this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom; this.baseGraphHeight = this.graphHeight - 50; this.baseGraphWidth = this.graphWidth; // pixel offsets inside the svg and outside are not 1:1 - this.realPixelRatio = this.$refs.baseSvg.clientWidth / this.baseGraphWidth; + this.realPixelRatio = svgWidth / this.baseGraphWidth; this.renderAxesPaths(); this.formatDeployments(); diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 1369b5820d5..90fe339e3de 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -16,7 +16,7 @@ import 'vendor/jquery.atwho'; import AjaxCache from '~/lib/utils/ajax_cache'; import Vue from 'vue'; import syntaxHighlight from '~/syntax_highlight'; -import { SkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; import axios from './lib/utils/axios_utils'; import { getLocationHash } from './lib/utils/url_utility'; import Flash from './flash'; @@ -1293,10 +1293,10 @@ export default class Notes { new Vue({ el, components: { - SkeletonLoading, + GlSkeletonLoading, }, render(createElement) { - return createElement('skeleton-loading'); + return createElement('gl-skeleton-loading'); }, }); } diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue index d9e99603238..eaa0cded224 100644 --- a/app/assets/javascripts/notes/components/diff_with_note.vue +++ b/app/assets/javascripts/notes/components/diff_with_note.vue @@ -3,13 +3,13 @@ import { mapState, mapActions } from 'vuex'; import imageDiffHelper from '~/image_diff/helpers/index'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; -import { SkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; import { trimFirstCharOfLineContent } from '~/diffs/store/utils'; export default { components: { DiffFileHeader, - SkeletonLoading, + GlSkeletonLoading, }, props: { discussion: { @@ -143,7 +143,7 @@ export default { class="line_content js-success-lazy-load" > <span></span> - <skeleton-loading /> + <gl-skeleton-loading /> <span></span> </td> </tr> diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue index 27972682ca1..6e8f43048d1 100644 --- a/app/assets/javascripts/notes/components/discussion_filter.vue +++ b/app/assets/javascripts/notes/components/discussion_filter.vue @@ -22,9 +22,7 @@ export default { return { currentValue: this.defaultValue }; }, computed: { - ...mapGetters([ - 'getNotesDataByProp', - ]), + ...mapGetters(['getNotesDataByProp']), currentFilter() { if (!this.currentValue) return this.filters[0]; return this.filters.find(filter => filter.value === this.currentValue); @@ -51,7 +49,7 @@ export default { <button id="discussion-filter-dropdown" ref="dropdownToggle" - class="btn btn-default" + class="btn btn-default qa-discussion-filter" data-toggle="dropdown" aria-expanded="false" > @@ -69,6 +67,7 @@ export default { > <button :class="{ 'is-active': filter.value === currentValue }" + class="qa-filter-options" type="button" @click="selectFilter(filter.value)" > diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue index c68860d98ae..df7ab4502a6 100644 --- a/app/assets/javascripts/notes/components/note_awards_list.vue +++ b/app/assets/javascripts/notes/components/note_awards_list.vue @@ -95,31 +95,22 @@ export default { return awardList.filter(award => award.user.id === this.getUserData.id).length; }, awardTitle(awardsList) { - const hasReactionByCurrentUser = this.hasReactionByCurrentUser( - awardsList, - ); + const hasReactionByCurrentUser = this.hasReactionByCurrentUser(awardsList); const TOOLTIP_NAME_COUNT = hasReactionByCurrentUser ? 9 : 10; let awardList = awardsList; // Filter myself from list if I am awarded. if (hasReactionByCurrentUser) { - awardList = awardList.filter( - award => award.user.id !== this.getUserData.id, - ); + awardList = awardList.filter(award => award.user.id !== this.getUserData.id); } // Get only 9-10 usernames to show in tooltip text. - const namesToShow = awardList - .slice(0, TOOLTIP_NAME_COUNT) - .map(award => award.user.name); + const namesToShow = awardList.slice(0, TOOLTIP_NAME_COUNT).map(award => award.user.name); // Get the remaining list to use in `and x more` text. - const remainingAwardList = awardList.slice( - TOOLTIP_NAME_COUNT, - awardList.length, - ); + const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length); - // Add myself to the begining of the list so title will start with You. + // Add myself to the beginning of the list so title will start with You. if (hasReactionByCurrentUser) { namesToShow.unshift('You'); } @@ -128,9 +119,7 @@ export default { // We have 10+ awarded user, join them with comma and add `and x more`. if (remainingAwardList.length) { - title = `${namesToShow.join(', ')}, and ${ - remainingAwardList.length - } more.`; + title = `${namesToShow.join(', ')}, and ${remainingAwardList.length} more.`; } else if (namesToShow.length > 1) { // Join all names with comma but not the last one, it will be added with and text. title = namesToShow.slice(0, namesToShow.length - 1).join(', '); @@ -170,9 +159,7 @@ export default { awardName: parsedName, }; - this.toggleAwardRequest(data).catch(() => - Flash('Something went wrong on our end.'), - ); + this.toggleAwardRequest(data).catch(() => Flash('Something went wrong on our end.')); }, }, }; diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index b0faa443a18..7514ce8a1eb 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -54,7 +54,13 @@ export default { }; }, computed: { - ...mapGetters(['isNotesFetched', 'discussions', 'getNotesDataByProp', 'discussionCount', 'isLoading']), + ...mapGetters([ + 'isNotesFetched', + 'discussions', + 'getNotesDataByProp', + 'discussionCount', + 'isLoading', + ]), noteableType() { return this.noteableData.noteableType; }, diff --git a/app/assets/javascripts/notes/discussion_filters.js b/app/assets/javascripts/notes/discussion_filters.js index 012ffc4093e..06eadaeea0e 100644 --- a/app/assets/javascripts/notes/discussion_filters.js +++ b/app/assets/javascripts/notes/discussion_filters.js @@ -1,15 +1,17 @@ import Vue from 'vue'; import DiscussionFilter from './components/discussion_filter.vue'; -export default (store) => { +export default store => { const discussionFilterEl = document.getElementById('js-vue-discussion-filter'); if (discussionFilterEl) { const { defaultFilter, notesFilters } = discussionFilterEl.dataset; const defaultValue = defaultFilter ? parseInt(defaultFilter, 10) : null; const filterValues = notesFilters ? JSON.parse(notesFilters) : {}; - const filters = Object.keys(filterValues).map(entry => - ({ title: entry, value: filterValues[entry] })); + const filters = Object.keys(filterValues).map(entry => ({ + title: entry, + value: filterValues[entry], + })); return new Vue({ el: discussionFilterEl, diff --git a/app/assets/javascripts/notes/stores/collapse_utils.js b/app/assets/javascripts/notes/stores/collapse_utils.js index fa4a1c56b20..bee6d4f0329 100644 --- a/app/assets/javascripts/notes/stores/collapse_utils.js +++ b/app/assets/javascripts/notes/stores/collapse_utils.js @@ -68,12 +68,9 @@ export const collapseSystemNotes = notes => { lastDescriptionSystemNote = note; lastDescriptionSystemNoteIndex = acc.length; } else if (lastDescriptionSystemNote) { - const timeDifferenceMinutes = getTimeDifferenceMinutes( - lastDescriptionSystemNote, - note, - ); + const timeDifferenceMinutes = getTimeDifferenceMinutes(lastDescriptionSystemNote, note); - // are they less than 10 minutes appart? + // are they less than 10 minutes apart? if (timeDifferenceMinutes > 10) { // reset counter descriptionChangedTimes = 1; diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js index 21c334a9d33..e4f36154fcd 100644 --- a/app/assets/javascripts/notes/stores/getters.js +++ b/app/assets/javascripts/notes/stores/getters.js @@ -1,6 +1,5 @@ import _ from 'underscore'; import * as constants from '../constants'; -import { reduceDiscussionsToLineCodes } from './utils'; import { collapseSystemNotes } from './collapse_utils'; export const discussions = state => collapseSystemNotes(state.discussions); @@ -31,9 +30,6 @@ export const notesById = state => return acc; }, {}); -export const discussionsStructuredByLineCode = state => - reduceDiscussionsToLineCodes(state.discussions); - export const noteableType = state => { const { ISSUE_NOTEABLE_TYPE, MERGE_REQUEST_NOTEABLE_TYPE, EPIC_NOTEABLE_TYPE } = constants; diff --git a/app/assets/javascripts/notes/stores/index.js b/app/assets/javascripts/notes/stores/index.js index f105b7d0d11..d41b02b4a4b 100644 --- a/app/assets/javascripts/notes/stores/index.js +++ b/app/assets/javascripts/notes/stores/index.js @@ -4,5 +4,4 @@ import notesModule from './modules'; Vue.use(Vuex); -export default () => - new Vuex.Store(notesModule()); +export default () => new Vuex.Store(notesModule()); diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js index 0e41ff03d67..dd57539e4d8 100644 --- a/app/assets/javascripts/notes/stores/utils.js +++ b/app/assets/javascripts/notes/stores/utils.js @@ -25,18 +25,6 @@ export const getQuickActionText = note => { return text; }; -export const reduceDiscussionsToLineCodes = selectedDiscussions => - selectedDiscussions.reduce((acc, note) => { - if (note.diff_discussion && note.line_code) { - // For context about line notes: there might be multiple notes with the same line code - const items = acc[note.line_code] || []; - items.push(note); - - Object.assign(acc, { [note.line_code]: items }); - } - return acc; - }, {}); - export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note); export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim(); diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index 3b58c54b3f4..386a9b2c740 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -7,14 +7,21 @@ const ENDLESS_SCROLL_BOTTOM_PX = 400; const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000; export default { - init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) { + init( + limit = 0, + preload = false, + disable = false, + prepareData = $.noop, + callback = $.noop, + container = '', + ) { this.url = $('.content_list').data('href') || removeParams(['limit', 'offset']); this.limit = limit; this.offset = parseInt(getParameterByName('offset'), 10) || this.limit; this.disable = disable; this.prepareData = prepareData; this.callback = callback; - this.loading = $('.loading').first(); + this.loading = $(`${container} .loading`).first(); if (preload) { this.offset = 0; this.getOld(); diff --git a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js index 15e737fff05..d9cf62db3f7 100644 --- a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js +++ b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js @@ -31,7 +31,7 @@ export default class AbuseReports { $messageCellElement.text(originalMessage); } else { $messageCellElement.data('messageTruncated', 'true'); - $messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`); + $messageCellElement.text(`${originalMessage.substr(0, MAX_MESSAGE_LENGTH - 3)}...`); } } } diff --git a/app/assets/javascripts/pages/admin/admin.js b/app/assets/javascripts/pages/admin/admin.js index ff4d6ab15f9..4616a075729 100644 --- a/app/assets/javascripts/pages/admin/admin.js +++ b/app/assets/javascripts/pages/admin/admin.js @@ -23,7 +23,7 @@ export default function adminInit() { } }); - $('body').on('click', '.js-toggle-colors-link', (e) => { + $('body').on('click', '.js-toggle-colors-link', e => { e.preventDefault(); $('.js-toggle-colors-container').toggleClass('hide'); }); @@ -33,12 +33,15 @@ export default function adminInit() { $(this).tab('show'); }); - $('.log-bottom').on('click', (e) => { + $('.log-bottom').on('click', e => { e.preventDefault(); const $visibleLog = $('.file-content:visible'); - $visibleLog.animate({ - scrollTop: $visibleLog.find('ol').height(), - }, 'fast'); + $visibleLog.animate( + { + scrollTop: $visibleLog.find('ol').height(), + }, + 'fast', + ); }); $('.change-owner-link').on('click', function changeOwnerLinkClick(e) { @@ -47,7 +50,7 @@ export default function adminInit() { modal.show(); }); - $('.change-owner-cancel-link').on('click', (e) => { + $('.change-owner-cancel-link').on('click', e => { e.preventDefault(); modal.hide(); $('.change-owner-link').show(); diff --git a/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js b/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js index 7281f907ec7..455c637a6b3 100644 --- a/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js +++ b/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js @@ -1,10 +1,14 @@ import { __ } from '~/locale'; export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE = __('Regex pattern'); -export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE = __('To define internal users, first enable new users set to external'); +export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE = __( + 'To define internal users, first enable new users set to external', +); function setUserInternalRegexPlaceholder(checkbox) { - const userInternalRegex = document.getElementById('application_setting_user_default_internal_regex'); + const userInternalRegex = document.getElementById( + 'application_setting_user_default_internal_regex', + ); if (checkbox && userInternalRegex) { if (checkbox.checked) { userInternalRegex.readOnly = false; diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js index e7ceccb6f47..d5ded3f9a79 100644 --- a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js +++ b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js @@ -17,20 +17,24 @@ export default () => { const previewPath = $('textarea#broadcast_message_message').data('previewPath'); - $('textarea#broadcast_message_message').on('input', _.debounce(function onMessageInput() { - const message = $(this).val(); - if (message === '') { - $('.js-broadcast-message-preview').text('Your message here'); - } else { - axios.post(previewPath, { - broadcast_message: { - message, - }, - }) - .then(({ data }) => { - $('.js-broadcast-message-preview').html(data.message); - }) - .catch(() => flash(__('An error occurred while rendering preview broadcast message'))); - } - }, 250)); + $('textarea#broadcast_message_message').on( + 'input', + _.debounce(function onMessageInput() { + const message = $(this).val(); + if (message === '') { + $('.js-broadcast-message-preview').text('Your message here'); + } else { + axios + .post(previewPath, { + broadcast_message: { + message, + }, + }) + .then(({ data }) => { + $('.js-broadcast-message-preview').html(data.message); + }) + .catch(() => flash(__('An error occurred while rendering preview broadcast message'))); + } + }, 250), + ); }; diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue index bc84666779e..e2fec3c7172 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue +++ b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue @@ -1,39 +1,42 @@ <script> - import axios from '~/lib/utils/axios_utils'; - import createFlash from '~/flash'; - import GlModal from '~/vue_shared/components/gl_modal.vue'; - import { redirectTo } from '~/lib/utils/url_utility'; - import { s__ } from '~/locale'; +import axios from '~/lib/utils/axios_utils'; +import createFlash from '~/flash'; +import GlModal from '~/vue_shared/components/gl_modal.vue'; +import { redirectTo } from '~/lib/utils/url_utility'; +import { s__ } from '~/locale'; - export default { - components: { - GlModal, +export default { + components: { + GlModal, + }, + props: { + url: { + type: String, + required: true, }, - props: { - url: { - type: String, - required: true, - }, + }, + computed: { + text() { + return s__( + 'AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running.', + ); }, - computed: { - text() { - return s__('AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running.'); - }, + }, + methods: { + onSubmit() { + return axios + .post(this.url) + .then(response => { + // follow the rediect to refresh the page + redirectTo(response.request.responseURL); + }) + .catch(error => { + createFlash(s__('AdminArea|Stopping jobs failed')); + throw error; + }); }, - methods: { - onSubmit() { - return axios.post(this.url) - .then((response) => { - // follow the rediect to refresh the page - redirectTo(response.request.responseURL); - }) - .catch((error) => { - createFlash(s__('AdminArea|Stopping jobs failed')); - throw error; - }); - }, - }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/pages/admin/projects/index.js b/app/assets/javascripts/pages/admin/projects/index.js index 31c96eb87af..d6b1e747aec 100644 --- a/app/assets/javascripts/pages/admin/projects/index.js +++ b/app/assets/javascripts/pages/admin/projects/index.js @@ -4,6 +4,7 @@ import NamespaceSelect from '../../../namespace_select'; document.addEventListener('DOMContentLoaded', () => { new ProjectsList(); // eslint-disable-line no-new - document.querySelectorAll('.js-namespace-select') + document + .querySelectorAll('.js-namespace-select') .forEach(dropdown => new NamespaceSelect({ dropdown })); }); diff --git a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue index ff66d3a8ac4..3c383735f4a 100644 --- a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue +++ b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue @@ -1,81 +1,84 @@ <script> - import _ from 'underscore'; - import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; - import { s__, sprintf } from '~/locale'; +import _ from 'underscore'; +import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; +import { s__, sprintf } from '~/locale'; - export default { - components: { - DeprecatedModal, +export default { + components: { + DeprecatedModal, + }, + props: { + deleteProjectUrl: { + type: String, + required: false, + default: '', }, - props: { - deleteProjectUrl: { - type: String, - required: false, - default: '', - }, - projectName: { - type: String, - required: false, - default: '', - }, - csrfToken: { - type: String, - required: false, - default: '', - }, + projectName: { + type: String, + required: false, + default: '', }, - data() { - return { - enteredProjectName: '', - }; + csrfToken: { + type: String, + required: false, + default: '', }, - computed: { - title() { - return sprintf(s__('AdminProjects|Delete Project %{projectName}?'), - { - projectName: `'${_.escape(this.projectName)}'`, - }, - false, - ); - }, - text() { - return sprintf(s__(`AdminProjects| + }, + data() { + return { + enteredProjectName: '', + }; + }, + computed: { + title() { + return sprintf( + s__('AdminProjects|Delete Project %{projectName}?'), + { + projectName: `'${_.escape(this.projectName)}'`, + }, + false, + ); + }, + text() { + return sprintf( + s__(`AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered.`), - { - projectName: `<strong>${_.escape(this.projectName)}</strong>`, - strong_start: '<strong>', - strong_end: '</strong>', - }, - false, - ); - }, - confirmationTextLabel() { - return sprintf(s__('AdminUsers|To confirm, type %{projectName}'), - { - projectName: `<code>${_.escape(this.projectName)}</code>`, - }, - false, - ); - }, - primaryButtonLabel() { - return s__('AdminProjects|Delete project'); - }, - canSubmit() { - return this.enteredProjectName === this.projectName; - }, + { + projectName: `<strong>${_.escape(this.projectName)}</strong>`, + strong_start: '<strong>', + strong_end: '</strong>', + }, + false, + ); + }, + confirmationTextLabel() { + return sprintf( + s__('AdminUsers|To confirm, type %{projectName}'), + { + projectName: `<code>${_.escape(this.projectName)}</code>`, + }, + false, + ); + }, + primaryButtonLabel() { + return s__('AdminProjects|Delete project'); + }, + canSubmit() { + return this.enteredProjectName === this.projectName; + }, + }, + methods: { + onCancel() { + this.enteredProjectName = ''; }, - methods: { - onCancel() { - this.enteredProjectName = ''; - }, - onSubmit() { - this.$refs.form.submit(); - this.enteredProjectName = ''; - }, + onSubmit() { + this.$refs.form.submit(); + this.enteredProjectName = ''; }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/pages/admin/projects/index/index.js b/app/assets/javascripts/pages/admin/projects/index/index.js index ddbefec87b6..6fa8760545d 100644 --- a/app/assets/javascripts/pages/admin/projects/index/index.js +++ b/app/assets/javascripts/pages/admin/projects/index/index.js @@ -28,7 +28,7 @@ document.addEventListener('DOMContentLoaded', () => { }, }); - $(document).on('shown.bs.modal', (event) => { + $(document).on('shown.bs.modal', event => { if (event.relatedTarget.classList.contains('delete-project-button')) { const buttonProps = event.relatedTarget.dataset; deleteModal.deleteProjectUrl = buttonProps.deleteProjectUrl; diff --git a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue index 8d5efcdcd96..4b33fcc759a 100644 --- a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue +++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue @@ -1,114 +1,119 @@ <script> - import _ from 'underscore'; - import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; - import { s__, sprintf } from '~/locale'; +import _ from 'underscore'; +import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; +import { s__, sprintf } from '~/locale'; - export default { - components: { - DeprecatedModal, +export default { + components: { + DeprecatedModal, + }, + props: { + deleteUserUrl: { + type: String, + required: false, + default: '', }, - props: { - deleteUserUrl: { - type: String, - required: false, - default: '', - }, - blockUserUrl: { - type: String, - required: false, - default: '', - }, - deleteContributions: { - type: Boolean, - required: false, - default: false, - }, - username: { - type: String, - required: false, - default: '', - }, - csrfToken: { - type: String, - required: false, - default: '', - }, + blockUserUrl: { + type: String, + required: false, + default: '', }, - data() { - return { - enteredUsername: '', - }; + deleteContributions: { + type: Boolean, + required: false, + default: false, }, - computed: { - title() { - const keepContributionsTitle = s__('AdminUsers|Delete User %{username}?'); - const deleteContributionsTitle = s__('AdminUsers|Delete User %{username} and contributions?'); + username: { + type: String, + required: false, + default: '', + }, + csrfToken: { + type: String, + required: false, + default: '', + }, + }, + data() { + return { + enteredUsername: '', + }; + }, + computed: { + title() { + const keepContributionsTitle = s__('AdminUsers|Delete User %{username}?'); + const deleteContributionsTitle = s__('AdminUsers|Delete User %{username} and contributions?'); - return sprintf( - this.deleteContributions ? deleteContributionsTitle : keepContributionsTitle, { - username: `'${_.escape(this.username)}'`, - }, false); - }, - text() { - const keepContributionsText = s__(`AdminArea| + return sprintf( + this.deleteContributions ? deleteContributionsTitle : keepContributionsTitle, + { + username: `'${_.escape(this.username)}'`, + }, + false, + ); + }, + text() { + const keepContributionsText = s__(`AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide "Ghost-user". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered.`); - const deleteContributionsText = s__(`AdminArea| + const deleteContributionsText = s__(`AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered.`); - return sprintf(this.deleteContributions ? deleteContributionsText : keepContributionsText, - { - username: `<strong>${_.escape(this.username)}</strong>`, - strong_start: '<strong>', - strong_end: '</strong>', - }, - false, - ); - }, - confirmationTextLabel() { - return sprintf(s__('AdminUsers|To confirm, type %{username}'), - { - username: `<code>${_.escape(this.username)}</code>`, - }, - false, - ); - }, - primaryButtonLabel() { - const keepContributionsLabel = s__('AdminUsers|Delete user'); - const deleteContributionsLabel = s__('AdminUsers|Delete user and contributions'); + return sprintf( + this.deleteContributions ? deleteContributionsText : keepContributionsText, + { + username: `<strong>${_.escape(this.username)}</strong>`, + strong_start: '<strong>', + strong_end: '</strong>', + }, + false, + ); + }, + confirmationTextLabel() { + return sprintf( + s__('AdminUsers|To confirm, type %{username}'), + { + username: `<code>${_.escape(this.username)}</code>`, + }, + false, + ); + }, + primaryButtonLabel() { + const keepContributionsLabel = s__('AdminUsers|Delete user'); + const deleteContributionsLabel = s__('AdminUsers|Delete user and contributions'); - return this.deleteContributions ? deleteContributionsLabel : keepContributionsLabel; - }, - secondaryButtonLabel() { - return s__('AdminUsers|Block user'); - }, - canSubmit() { - return this.enteredUsername === this.username; - }, + return this.deleteContributions ? deleteContributionsLabel : keepContributionsLabel; }, - methods: { - onCancel() { - this.enteredUsername = ''; - }, - onSecondaryAction() { - const { form } = this.$refs; + secondaryButtonLabel() { + return s__('AdminUsers|Block user'); + }, + canSubmit() { + return this.enteredUsername === this.username; + }, + }, + methods: { + onCancel() { + this.enteredUsername = ''; + }, + onSecondaryAction() { + const { form } = this.$refs; - form.action = this.blockUserUrl; - this.$refs.method.value = 'put'; + form.action = this.blockUserUrl; + this.$refs.method.value = 'put'; - form.submit(); - }, - onSubmit() { - this.$refs.form.submit(); - this.enteredUsername = ''; - }, + form.submit(); + }, + onSubmit() { + this.$refs.form.submit(); + this.enteredUsername = ''; }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/pages/admin/users/index.js b/app/assets/javascripts/pages/admin/users/index.js index 06599c3fd5f..45046688b57 100644 --- a/app/assets/javascripts/pages/admin/users/index.js +++ b/app/assets/javascripts/pages/admin/users/index.js @@ -32,12 +32,14 @@ document.addEventListener('DOMContentLoaded', () => { }, }); - $(document).on('shown.bs.modal', (event) => { + $(document).on('shown.bs.modal', event => { if (event.relatedTarget.classList.contains('delete-user-button')) { const buttonProps = event.relatedTarget.dataset; deleteModal.deleteUserUrl = buttonProps.deleteUserUrl; deleteModal.blockUserUrl = buttonProps.blockUserUrl; - deleteModal.deleteContributions = event.relatedTarget.hasAttribute('data-delete-contributions'); + deleteModal.deleteContributions = event.relatedTarget.hasAttribute( + 'data-delete-contributions', + ); deleteModal.username = buttonProps.username; } }); diff --git a/app/assets/javascripts/pages/admin/users/new/index.js b/app/assets/javascripts/pages/admin/users/new/index.js index 58bfa8d64e7..3e6a090cb0e 100644 --- a/app/assets/javascripts/pages/admin/users/new/index.js +++ b/app/assets/javascripts/pages/admin/users/new/index.js @@ -4,7 +4,9 @@ export default class UserInternalRegexHandler { constructor() { this.regexPattern = $('[data-user-internal-regex-pattern]').data('user-internal-regex-pattern'); if (this.regexPattern && this.regexPattern !== '') { - this.regexOptions = $('[data-user-internal-regex-options]').data('user-internal-regex-options'); + this.regexOptions = $('[data-user-internal-regex-options]').data( + 'user-internal-regex-options', + ); this.external = $('#user_external'); this.warningMessage = $('#warning_external_automatically_set'); this.addListenerToEmailField(); @@ -13,7 +15,7 @@ export default class UserInternalRegexHandler { } addListenerToEmailField() { - $('#user_email').on('input', (event) => { + $('#user_email').on('input', event => { this.setExternalCheckbox(event.currentTarget.value); }); } diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js index 72f3f70b98f..1b56b97f751 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js @@ -79,7 +79,8 @@ export default class Todos { .then(({ data }) => { this.updateRowState(target); this.updateBadges(data); - }).catch(() => { + }) + .catch(() => { this.updateRowState(target, true); return flash(__('Error updating todo status.')); }); @@ -118,10 +119,12 @@ export default class Todos { axios[target.dataset.method](target.dataset.href, { ids: this.todo_ids, - }).then(({ data }) => { - this.updateAllState(target, data); - this.updateBadges(data); - }).catch(() => flash(__('Error updating status for all todos.'))); + }) + .then(({ data }) => { + this.updateAllState(target, data); + this.updateBadges(data); + }) + .catch(() => flash(__('Error updating status for all todos.'))); } updateAllState(target, data) { @@ -133,7 +136,7 @@ export default class Todos { target.removeAttribute('disabled'); target.classList.remove('disabled'); - this.todo_ids = (target === markAllDoneBtn) ? data.updated_ids : []; + this.todo_ids = target === markAllDoneBtn ? data.updated_ids : []; undoAllBtn.classList.toggle('hidden'); markAllDoneBtn.classList.toggle('hidden'); todoListContainer.classList.toggle('hidden'); diff --git a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js index d3b2656743d..ae0a8c74964 100644 --- a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js @@ -9,7 +9,7 @@ document.addEventListener('DOMContentLoaded', () => { // eslint-disable-next-line no-new new AjaxVariableList({ container: variableListEl, - saveButton: variableListEl.querySelector('.js-secret-variables-save-button'), + saveButton: variableListEl.querySelector('.js-ci-variables-save-button'), errorBox: variableListEl.querySelector('.js-ci-variable-error-box'), saveEndpoint: variableListEl.dataset.saveEndpoint, }); diff --git a/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue index 48668562f09..a4778077bc4 100644 --- a/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue +++ b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue @@ -1,94 +1,117 @@ <script> - import axios from '~/lib/utils/axios_utils'; +import axios from '~/lib/utils/axios_utils'; - import Flash from '~/flash'; - import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; - import { n__, s__, sprintf } from '~/locale'; - import { redirectTo } from '~/lib/utils/url_utility'; - import eventHub from '../event_hub'; +import Flash from '~/flash'; +import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; +import { n__, s__, sprintf } from '~/locale'; +import { redirectTo } from '~/lib/utils/url_utility'; +import eventHub from '../event_hub'; - export default { - components: { - DeprecatedModal, +export default { + components: { + DeprecatedModal, + }, + props: { + issueCount: { + type: Number, + required: true, }, - props: { - issueCount: { - type: Number, - required: true, - }, - mergeRequestCount: { - type: Number, - required: true, - }, - milestoneId: { - type: Number, - required: true, - }, - milestoneTitle: { - type: String, - required: true, - }, - milestoneUrl: { - type: String, - required: true, - }, + mergeRequestCount: { + type: Number, + required: true, }, - computed: { - text() { - const milestoneTitle = sprintf('<strong>%{milestoneTitle}</strong>', { milestoneTitle: this.milestoneTitle }); - - if (this.issueCount === 0 && this.mergeRequestCount === 0) { - return sprintf( - s__(`Milestones| -You’re about to permanently delete the milestone %{milestoneTitle}. -This milestone is not currently used in any issues or merge requests.`), - { - milestoneTitle, - }, - false, - ); - } + milestoneId: { + type: Number, + required: true, + }, + milestoneTitle: { + type: String, + required: true, + }, + milestoneUrl: { + type: String, + required: true, + }, + }, + computed: { + text() { + const milestoneTitle = sprintf('<strong>%{milestoneTitle}</strong>', { + milestoneTitle: this.milestoneTitle, + }); + if (this.issueCount === 0 && this.mergeRequestCount === 0) { return sprintf( s__(`Milestones| -You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. -Once deleted, it cannot be undone or recovered.`), +You’re about to permanently delete the milestone %{milestoneTitle}. +This milestone is not currently used in any issues or merge requests.`), { milestoneTitle, - issuesWithCount: n__('%d issue', '%d issues', this.issueCount), - mergeRequestsWithCount: n__('%d merge request', '%d merge requests', this.mergeRequestCount), }, false, ); - }, - title() { - return sprintf(s__('Milestones|Delete milestone %{milestoneTitle}?'), { milestoneTitle: this.milestoneTitle }); - }, - }, - methods: { - onSubmit() { - eventHub.$emit('deleteMilestoneModal.requestStarted', this.milestoneUrl); + } - return axios.delete(this.milestoneUrl) - .then((response) => { - eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: true }); + return sprintf( + s__(`Milestones| +You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. +Once deleted, it cannot be undone or recovered.`), + { + milestoneTitle, + issuesWithCount: n__('%d issue', '%d issues', this.issueCount), + mergeRequestsWithCount: n__( + '%d merge request', + '%d merge requests', + this.mergeRequestCount, + ), + }, + false, + ); + }, + title() { + return sprintf(s__('Milestones|Delete milestone %{milestoneTitle}?'), { + milestoneTitle: this.milestoneTitle, + }); + }, + }, + methods: { + onSubmit() { + eventHub.$emit('deleteMilestoneModal.requestStarted', this.milestoneUrl); - // follow the rediect to milestones overview page - redirectTo(response.request.responseURL); - }) - .catch((error) => { - eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: false }); + return axios + .delete(this.milestoneUrl) + .then(response => { + eventHub.$emit('deleteMilestoneModal.requestFinished', { + milestoneUrl: this.milestoneUrl, + successful: true, + }); - if (error.response && error.response.status === 404) { - Flash(sprintf(s__('Milestones|Milestone %{milestoneTitle} was not found'), { milestoneTitle: this.milestoneTitle })); - } else { - Flash(sprintf(s__('Milestones|Failed to delete milestone %{milestoneTitle}'), { milestoneTitle: this.milestoneTitle })); - } - throw error; + // follow the rediect to milestones overview page + redirectTo(response.request.responseURL); + }) + .catch(error => { + eventHub.$emit('deleteMilestoneModal.requestFinished', { + milestoneUrl: this.milestoneUrl, + successful: false, }); - }, + + if (error.response && error.response.status === 404) { + Flash( + sprintf(s__('Milestones|Milestone %{milestoneTitle} was not found'), { + milestoneTitle: this.milestoneTitle, + }), + ); + } else { + Flash( + sprintf(s__('Milestones|Failed to delete milestone %{milestoneTitle}'), { + milestoneTitle: this.milestoneTitle, + }), + ); + } + throw error; + }); }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue index 2c683a39f42..9d19e4a095d 100644 --- a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue +++ b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue @@ -1,54 +1,66 @@ <script> - import axios from '~/lib/utils/axios_utils'; - import createFlash from '~/flash'; - import GlModal from '~/vue_shared/components/gl_modal.vue'; - import { s__, sprintf } from '~/locale'; - import { visitUrl } from '~/lib/utils/url_utility'; - import eventHub from '../event_hub'; +import axios from '~/lib/utils/axios_utils'; +import createFlash from '~/flash'; +import GlModal from '~/vue_shared/components/gl_modal.vue'; +import { s__, sprintf } from '~/locale'; +import { visitUrl } from '~/lib/utils/url_utility'; +import eventHub from '../event_hub'; - export default { - components: { - GlModal, +export default { + components: { + GlModal, + }, + props: { + milestoneTitle: { + type: String, + required: true, }, - props: { - milestoneTitle: { - type: String, - required: true, - }, - url: { - type: String, - required: true, - }, - groupName: { - type: String, - required: true, - }, + url: { + type: String, + required: true, }, - computed: { - title() { - return sprintf(s__('Milestones|Promote %{milestoneTitle} to group milestone?'), { milestoneTitle: this.milestoneTitle }); - }, - text() { - return sprintf(s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}. + groupName: { + type: String, + required: true, + }, + }, + computed: { + title() { + return sprintf(s__('Milestones|Promote %{milestoneTitle} to group milestone?'), { + milestoneTitle: this.milestoneTitle, + }); + }, + text() { + return sprintf( + s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}. Existing project milestones with the same title will be merged. - This action cannot be reversed.`), { milestoneTitle: this.milestoneTitle, groupName: this.groupName }); - }, + This action cannot be reversed.`), + { milestoneTitle: this.milestoneTitle, groupName: this.groupName }, + ); }, - methods: { - onSubmit() { - eventHub.$emit('promoteMilestoneModal.requestStarted', this.url); - return axios.post(this.url, { params: { format: 'json' } }) - .then((response) => { - eventHub.$emit('promoteMilestoneModal.requestFinished', { milestoneUrl: this.url, successful: true }); - visitUrl(response.data.url); - }) - .catch((error) => { - eventHub.$emit('promoteMilestoneModal.requestFinished', { milestoneUrl: this.url, successful: false }); - createFlash(error); + }, + methods: { + onSubmit() { + eventHub.$emit('promoteMilestoneModal.requestStarted', this.url); + return axios + .post(this.url, { params: { format: 'json' } }) + .then(response => { + eventHub.$emit('promoteMilestoneModal.requestFinished', { + milestoneUrl: this.url, + successful: true, }); - }, + visitUrl(response.data.url); + }) + .catch(error => { + eventHub.$emit('promoteMilestoneModal.requestFinished', { + milestoneUrl: this.url, + successful: false, + }); + createFlash(error); + }); }, - }; + }, +}; </script> <template> <gl-modal @@ -65,4 +77,3 @@ {{ text }} </gl-modal> </template> - diff --git a/app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js b/app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js index d51b5c221e3..1d559dc6e41 100644 --- a/app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js +++ b/app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js @@ -7,7 +7,9 @@ export default () => { Vue.use(Translate); const onRequestFinished = ({ milestoneUrl, successful }) => { - const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`); + const button = document.querySelector( + `.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`, + ); if (!successful) { button.removeAttribute('disabled'); @@ -16,14 +18,16 @@ export default () => { button.querySelector('.js-loading-icon').classList.add('hidden'); }; - const onRequestStarted = (milestoneUrl) => { - const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`); + const onRequestStarted = milestoneUrl => { + const button = document.querySelector( + `.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`, + ); button.setAttribute('disabled', ''); button.querySelector('.js-loading-icon').classList.remove('hidden'); eventHub.$once('deleteMilestoneModal.requestFinished', onRequestFinished); }; - const onDeleteButtonClick = (event) => { + const onDeleteButtonClick = event => { const button = event.currentTarget; const modalProps = { milestoneId: parseInt(button.dataset.milestoneId, 10), @@ -37,12 +41,12 @@ export default () => { }; const deleteMilestoneButtons = document.querySelectorAll('.js-delete-milestone-button'); - deleteMilestoneButtons.forEach((button) => { + deleteMilestoneButtons.forEach(button => { button.addEventListener('click', onDeleteButtonClick); }); eventHub.$once('deleteMilestoneModal.mounted', () => { - deleteMilestoneButtons.forEach((button) => { + deleteMilestoneButtons.forEach(button => { button.removeAttribute('disabled'); }); }); diff --git a/app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js b/app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js index 8e79341e96a..fcc62a2b2af 100644 --- a/app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js +++ b/app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js @@ -7,20 +7,24 @@ Vue.use(Translate); export default () => { const onRequestFinished = ({ milestoneUrl, successful }) => { - const button = document.querySelector(`.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`); + const button = document.querySelector( + `.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`, + ); if (!successful) { button.removeAttribute('disabled'); } }; - const onRequestStarted = (milestoneUrl) => { - const button = document.querySelector(`.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`); + const onRequestStarted = milestoneUrl => { + const button = document.querySelector( + `.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`, + ); button.setAttribute('disabled', ''); eventHub.$once('promoteMilestoneModal.requestFinished', onRequestFinished); }; - const onDeleteButtonClick = (event) => { + const onDeleteButtonClick = event => { const button = event.currentTarget; const modalProps = { milestoneTitle: button.dataset.milestoneTitle, @@ -32,12 +36,12 @@ export default () => { }; const promoteMilestoneButtons = document.querySelectorAll('.js-promote-project-milestone-button'); - promoteMilestoneButtons.forEach((button) => { + promoteMilestoneButtons.forEach(button => { button.addEventListener('click', onDeleteButtonClick); }); eventHub.$once('promoteMilestoneModal.mounted', () => { - promoteMilestoneButtons.forEach((button) => { + promoteMilestoneButtons.forEach(button => { button.removeAttribute('disabled'); }); }); diff --git a/app/assets/javascripts/pages/profiles/index.js b/app/assets/javascripts/pages/profiles/index.js index 04e50963699..883be18b336 100644 --- a/app/assets/javascripts/pages/profiles/index.js +++ b/app/assets/javascripts/pages/profiles/index.js @@ -3,9 +3,12 @@ import '~/profile/gl_crop'; import Profile from '~/profile/profile'; document.addEventListener('DOMContentLoaded', () => { - $(document).on('input.ssh_key', '#key_key', function () { // eslint-disable-line func-names + // eslint-disable-next-line func-names + $(document).on('input.ssh_key', '#key_key', function() { const $title = $('#key_title'); - const comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/); + const comment = $(this) + .val() + .match(/^\S+ \S+ (.+)\n?$/); // Extract the SSH Key title from its comment if (comment && comment.length > 1) { diff --git a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js index 8e8f47c21d8..417935e2ad0 100644 --- a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js +++ b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js @@ -5,7 +5,9 @@ document.addEventListener('DOMContentLoaded', () => { const twoFactorNode = document.querySelector('.js-two-factor-auth'); const skippable = twoFactorNode.dataset.twoFactorSkippable === 'true'; if (skippable) { - const button = `<a class="btn btn-sm btn-warning float-right" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a>`; + const button = `<a class="btn btn-sm btn-warning float-right" data-method="patch" href="${ + twoFactorNode.dataset.two_factor_skip_url + }">Configure it later</a>`; const flashAlert = document.querySelector('.flash-alert .container-fluid'); if (flashAlert) flashAlert.insertAdjacentHTML('beforeend', button); } diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js index a9658fd1eb4..13ff47d53c2 100644 --- a/app/assets/javascripts/pages/projects/branches/new/index.js +++ b/app/assets/javascripts/pages/projects/branches/new/index.js @@ -1,6 +1,11 @@ import $ from 'jquery'; import NewBranchForm from '~/new_branch_form'; -document.addEventListener('DOMContentLoaded', () => ( - new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)) -)); +document.addEventListener( + 'DOMContentLoaded', + () => + new NewBranchForm( + $('.js-create-branch-form'), + JSON.parse(document.getElementById('availableRefs').innerHTML), + ), +); diff --git a/app/assets/javascripts/pages/projects/graphs/charts/index.js b/app/assets/javascripts/pages/projects/graphs/charts/index.js index 80159a82bd4..3ccad513c05 100644 --- a/app/assets/javascripts/pages/projects/graphs/charts/index.js +++ b/app/assets/javascripts/pages/projects/graphs/charts/index.js @@ -31,14 +31,16 @@ document.addEventListener('DOMContentLoaded', () => { const chartData = data => ({ labels: Object.keys(data), - datasets: [{ - fillColor: 'rgba(220,220,220,0.5)', - strokeColor: 'rgba(220,220,220,1)', - barStrokeWidth: 1, - barValueSpacing: 1, - barDatasetSpacing: 1, - data: _.values(data), - }], + datasets: [ + { + fillColor: 'rgba(220,220,220,0.5)', + strokeColor: 'rgba(220,220,220,1)', + barStrokeWidth: 1, + barValueSpacing: 1, + barDatasetSpacing: 1, + data: _.values(data), + }, + ], }); const hourData = chartData(projectChartData.hour); @@ -51,7 +53,9 @@ document.addEventListener('DOMContentLoaded', () => { responsiveChart($('#month-chart'), monthData); const data = projectChartData.languages; - const ctx = $('#languages-chart').get(0).getContext('2d'); + const ctx = $('#languages-chart') + .get(0) + .getContext('2d'); const options = { scaleOverlay: true, responsive: true, diff --git a/app/assets/javascripts/pages/projects/graphs/show/index.js b/app/assets/javascripts/pages/projects/graphs/show/index.js index 71f629fbc13..f79c386b59e 100644 --- a/app/assets/javascripts/pages/projects/graphs/show/index.js +++ b/app/assets/javascripts/pages/projects/graphs/show/index.js @@ -7,7 +7,8 @@ import ContributorsStatGraph from './stat_graph_contributors'; document.addEventListener('DOMContentLoaded', () => { const url = document.querySelector('.js-graphs-show').dataset.projectGraphPath; - axios.get(url) + axios + .get(url) .then(({ data }) => { const graph = new ContributorsStatGraph(); graph.init(data); diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js index 58bb8c5b0c8..76613394af6 100644 --- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js @@ -3,7 +3,11 @@ import $ from 'jquery'; import _ from 'underscore'; import { n__, s__, createDateTimeFormat, sprintf } from '~/locale'; -import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph'; +import { + ContributorsGraph, + ContributorsAuthorGraph, + ContributorsMasterGraph, +} from './stat_graph_contributors_graph'; import ContributorsStatGraphUtil from './stat_graph_contributors_util'; export default (function() { @@ -14,7 +18,7 @@ export default (function() { ContributorsStatGraph.prototype.init = function(log) { var author_commits, total_commits; this.parsed_log = ContributorsStatGraphUtil.parse_log(log); - this.set_current_field("commits"); + this.set_current_field('commits'); total_commits = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field); author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field); this.add_master_graph(total_commits); @@ -31,23 +35,26 @@ export default (function() { var limited_author_data; this.authors = []; limited_author_data = author_data.slice(0, 100); - return _.each(limited_author_data, (function(_this) { - return function(d) { - var author_graph, author_header; - author_header = _this.create_author_header(d); - $(".contributors-list").append(author_header); - - author_graph = new ContributorsAuthorGraph(d.dates); - _this.authors[d.author_name] = author_graph; - return author_graph.draw(); - }; - })(this)); + return _.each( + limited_author_data, + (function(_this) { + return function(d) { + var author_graph, author_header; + author_header = _this.create_author_header(d); + $('.contributors-list').append(author_header); + + author_graph = new ContributorsAuthorGraph(d.dates); + _this.authors[d.author_name] = author_graph; + return author_graph.draw(); + }; + })(this), + ); }; ContributorsStatGraph.prototype.format_author_commit_info = function(author) { var commits; commits = $('<span/>', { - "class": 'graph-author-commits-count' + class: 'graph-author-commits-count', }); commits.text(n__('%d commit', '%d commits', author.commits)); return $('<span/>').append(commits); @@ -56,13 +63,13 @@ export default (function() { ContributorsStatGraph.prototype.create_author_header = function(author) { var author_commit_info, author_commit_info_span, author_email, author_name, list_item; list_item = $('<li/>', { - "class": 'person', - style: 'display: block;' + class: 'person', + style: 'display: block;', }); author_name = $('<h4>' + author.author_name + '</h4>'); author_email = $('<p class="graph-author-email">' + author.author_email + '</p>'); author_commit_info_span = $('<span/>', { - "class": 'commits' + class: 'commits', }); author_commit_info = this.format_author_commit_info(author); author_commit_info_span.html(author_commit_info); @@ -80,37 +87,41 @@ export default (function() { }; ContributorsStatGraph.prototype.redraw_authors = function() { - $("ol").html(""); + $('ol').html(''); const { x_domain } = ContributorsGraph.prototype; - const author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain); - - return _.each(author_commits, (function(_this) { - return function(d) { - _this.redraw_author_commit_info(d); - if (_this.authors[d.author_name] != null) { - $(_this.authors[d.author_name].list_item).appendTo("ol"); - _this.authors[d.author_name].set_data(d.dates); - return _this.authors[d.author_name].redraw(); - } - return ''; - }; - })(this)); + const author_commits = ContributorsStatGraphUtil.get_author_data( + this.parsed_log, + this.field, + x_domain, + ); + + return _.each( + author_commits, + (function(_this) { + return function(d) { + _this.redraw_author_commit_info(d); + if (_this.authors[d.author_name] != null) { + $(_this.authors[d.author_name].list_item).appendTo('ol'); + _this.authors[d.author_name].set_data(d.dates); + return _this.authors[d.author_name].redraw(); + } + return ''; + }; + })(this), + ); }; ContributorsStatGraph.prototype.set_current_field = function(field) { - return this.field = field; + return (this.field = field); }; ContributorsStatGraph.prototype.change_date_header = function() { const { x_domain } = ContributorsGraph.prototype; - const formattedDateRange = sprintf( - s__('ContributorsPage|%{startDate} – %{endDate}'), - { - startDate: this.dateFormat.format(new Date(x_domain[0])), - endDate: this.dateFormat.format(new Date(x_domain[1])), - }, - ); + const formattedDateRange = sprintf(s__('ContributorsPage|%{startDate} – %{endDate}'), { + startDate: this.dateFormat.format(new Date(x_domain[0])), + endDate: this.dateFormat.format(new Date(x_domain[1])), + }); return $('#date_header').text(formattedDateRange); }; @@ -120,7 +131,7 @@ export default (function() { if ($author != null) { author_list_item = $(this.authors[author.author_name].list_item); author_commit_info = this.format_author_commit_info(author); - return author_list_item.find("span").html(author_commit_info); + return author_list_item.find('span').html(author_commit_info); } return ''; }; diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js index 5f91686347a..377dce6c746 100644 --- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js @@ -11,10 +11,32 @@ import { brushX } from 'd3-brush'; import { timeParse } from 'd3-time-format'; import { dateTickFormat } from '~/lib/utils/tick_formats'; -const d3 = { extent, max, select, scaleTime, scaleLinear, axisLeft, axisBottom, area, brushX, timeParse }; +const d3 = { + extent, + max, + select, + scaleTime, + scaleLinear, + axisLeft, + axisBottom, + area, + brushX, + timeParse, +}; const hasProp = {}.hasOwnProperty; -const extend = function(child, parent) { for (const key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; +const extend = function(child, parent) { + for (const key in parent) { + if (hasProp.call(parent, key)) child[key] = parent[key]; + } + function ctor() { + this.constructor = child; + } + ctor.prototype = parent.prototype; + child.prototype = new ctor(); + child.__super__ = parent.prototype; + return child; +}; export const ContributorsGraph = (function() { function ContributorsGraph() {} @@ -23,7 +45,7 @@ export const ContributorsGraph = (function() { top: 20, right: 10, bottom: 30, - left: 40 + left: 40, }; ContributorsGraph.prototype.x_domain = null; @@ -33,35 +55,39 @@ export const ContributorsGraph = (function() { ContributorsGraph.prototype.dates = []; ContributorsGraph.prototype.determine_width = function(baseWidth, $parentElement) { - const parentPaddingWidth = parseFloat($parentElement.css('padding-left')) + parseFloat($parentElement.css('padding-right')); + const parentPaddingWidth = + parseFloat($parentElement.css('padding-left')) + + parseFloat($parentElement.css('padding-right')); const marginWidth = this.MARGIN.left + this.MARGIN.right; return baseWidth - parentPaddingWidth - marginWidth; }; ContributorsGraph.set_x_domain = function(data) { - return ContributorsGraph.prototype.x_domain = data; + return (ContributorsGraph.prototype.x_domain = data); }; ContributorsGraph.set_y_domain = function(data) { - return ContributorsGraph.prototype.y_domain = [ - 0, d3.max(data, function(d) { - return d.commits = d.commits || d.additions || d.deletions; - }) - ]; + return (ContributorsGraph.prototype.y_domain = [ + 0, + d3.max(data, function(d) { + return (d.commits = d.commits || d.additions || d.deletions); + }), + ]); }; ContributorsGraph.init_x_domain = function(data) { - return ContributorsGraph.prototype.x_domain = d3.extent(data, function(d) { + return (ContributorsGraph.prototype.x_domain = d3.extent(data, function(d) { return d.date; - }); + })); }; ContributorsGraph.init_y_domain = function(data) { - return ContributorsGraph.prototype.y_domain = [ - 0, d3.max(data, function(d) { - return d.commits = d.commits || d.additions || d.deletions; - }) - ]; + return (ContributorsGraph.prototype.y_domain = [ + 0, + d3.max(data, function(d) { + return (d.commits = d.commits || d.additions || d.deletions); + }), + ]); }; ContributorsGraph.init_domain = function(data) { @@ -70,7 +96,7 @@ export const ContributorsGraph = (function() { }; ContributorsGraph.set_dates = function(data) { - return ContributorsGraph.prototype.dates = data; + return (ContributorsGraph.prototype.dates = data); }; ContributorsGraph.prototype.set_x_domain = function() { @@ -87,20 +113,33 @@ export const ContributorsGraph = (function() { }; ContributorsGraph.prototype.create_scale = function(width, height) { - this.x = d3.scaleTime().range([0, width]).clamp(true); - return this.y = d3.scaleLinear().range([height, 0]).nice(); + this.x = d3 + .scaleTime() + .range([0, width]) + .clamp(true); + return (this.y = d3 + .scaleLinear() + .range([height, 0]) + .nice()); }; ContributorsGraph.prototype.draw_x_axis = function() { - return this.svg.append("g").attr("class", "x axis").attr("transform", "translate(0, " + this.height + ")").call(this.x_axis); + return this.svg + .append('g') + .attr('class', 'x axis') + .attr('transform', 'translate(0, ' + this.height + ')') + .call(this.x_axis); }; ContributorsGraph.prototype.draw_y_axis = function() { - return this.svg.append("g").attr("class", "y axis").call(this.y_axis); + return this.svg + .append('g') + .attr('class', 'y axis') + .call(this.y_axis); }; ContributorsGraph.prototype.set_data = function(data) { - return this.data = data; + return (this.data = data); }; return ContributorsGraph; @@ -137,9 +176,9 @@ export const ContributorsMasterGraph = (function(superClass) { }; ContributorsMasterGraph.prototype.parse_dates = function(data) { - const parseDate = d3.timeParse("%Y-%m-%d"); + const parseDate = d3.timeParse('%Y-%m-%d'); return data.forEach(function(d) { - return d.date = parseDate(d.date); + return (d.date = parseDate(d.date)); }); }; @@ -148,42 +187,63 @@ export const ContributorsMasterGraph = (function(superClass) { }; ContributorsMasterGraph.prototype.create_axes = function() { - this.x_axis = d3.axisBottom() + this.x_axis = d3 + .axisBottom() .scale(this.x) .tickFormat(dateTickFormat); - return this.y_axis = d3.axisLeft().scale(this.y).ticks(5); + return (this.y_axis = d3 + .axisLeft() + .scale(this.y) + .ticks(5)); }; ContributorsMasterGraph.prototype.create_svg = function() { - this.svg = d3.select("#contributors-master") - .append("svg") - .attr("width", this.width + this.MARGIN.left + this.MARGIN.right) - .attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom) - .attr("class", "tint-box") - .append("g") - .attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")"); + this.svg = d3 + .select('#contributors-master') + .append('svg') + .attr('width', this.width + this.MARGIN.left + this.MARGIN.right) + .attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom) + .attr('class', 'tint-box') + .append('g') + .attr('transform', 'translate(' + this.MARGIN.left + ',' + this.MARGIN.top + ')'); return this.svg; }; ContributorsMasterGraph.prototype.create_area = function(x, y) { - return this.area = d3.area().x(function(d) { - return x(d.date); - }).y0(this.height).y1(function(d) { - d.commits = d.commits || d.additions || d.deletions; - return y(d.commits); - }); + return (this.area = d3 + .area() + .x(function(d) { + return x(d.date); + }) + .y0(this.height) + .y1(function(d) { + d.commits = d.commits || d.additions || d.deletions; + return y(d.commits); + })); }; ContributorsMasterGraph.prototype.create_brush = function() { - return this.brush = d3.brushX(this.x).extent([[this.x.range()[0], 0], [this.x.range()[1], this.height]]).on("end", this.update_content); + return (this.brush = d3 + .brushX(this.x) + .extent([[this.x.range()[0], 0], [this.x.range()[1], this.height]]) + .on('end', this.update_content)); }; ContributorsMasterGraph.prototype.draw_path = function(data) { - return this.svg.append("path").datum(data).attr("class", "area").attr("d", this.area); + return this.svg + .append('path') + .datum(data) + .attr('class', 'area') + .attr('d', this.area); }; ContributorsMasterGraph.prototype.add_brush = function() { - return this.svg.append("g").attr("class", "selection").call(this.brush).selectAll("rect").attr("height", this.height); + return this.svg + .append('g') + .attr('class', 'selection') + .call(this.brush) + .selectAll('rect') + .attr('height', this.height); }; ContributorsMasterGraph.prototype.update_content = function() { @@ -193,7 +253,7 @@ export const ContributorsMasterGraph = (function(superClass) { } else { ContributorsGraph.set_x_domain(this.x_max_domain); } - return $("#brush_change").trigger('change'); + return $('#brush_change').trigger('change'); }; ContributorsMasterGraph.prototype.draw = function() { @@ -216,9 +276,9 @@ export const ContributorsMasterGraph = (function(superClass) { this.process_dates(this.data); ContributorsGraph.set_y_domain(this.data); this.set_y_domain(); - this.svg.select("path").datum(this.data); - this.svg.select("path").attr("d", this.area); - return this.svg.select(".y.axis").call(this.y_axis); + this.svg.select('path').datum(this.data); + this.svg.select('path').attr('d', this.area); + return this.svg.select('.y.axis').call(this.y_axis); }; return ContributorsMasterGraph; @@ -252,43 +312,58 @@ export const ContributorsAuthorGraph = (function(superClass) { }; ContributorsAuthorGraph.prototype.create_axes = function() { - this.x_axis = d3.axisBottom() + this.x_axis = d3 + .axisBottom() .scale(this.x) .ticks(8) .tickFormat(dateTickFormat); - return this.y_axis = d3.axisLeft().scale(this.y).ticks(5); + return (this.y_axis = d3 + .axisLeft() + .scale(this.y) + .ticks(5)); }; ContributorsAuthorGraph.prototype.create_area = function(x, y) { - return this.area = d3.area().x(function(d) { - const parseDate = d3.timeParse("%Y-%m-%d"); - return x(parseDate(d)); - }).y0(this.height).y1((function(_this) { - return function(d) { - if (_this.data[d] != null) { - return y(_this.data[d]); - } else { - return y(0); - } - }; - })(this)); + return (this.area = d3 + .area() + .x(function(d) { + const parseDate = d3.timeParse('%Y-%m-%d'); + return x(parseDate(d)); + }) + .y0(this.height) + .y1( + (function(_this) { + return function(d) { + if (_this.data[d] != null) { + return y(_this.data[d]); + } else { + return y(0); + } + }; + })(this), + )); }; ContributorsAuthorGraph.prototype.create_svg = function() { const persons = document.querySelectorAll('.person'); this.list_item = persons[persons.length - 1]; - this.svg = d3.select(this.list_item) - .append("svg") - .attr("width", this.width + this.MARGIN.left + this.MARGIN.right) - .attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom) - .attr("class", "spark") - .append("g") - .attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")"); + this.svg = d3 + .select(this.list_item) + .append('svg') + .attr('width', this.width + this.MARGIN.left + this.MARGIN.right) + .attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom) + .attr('class', 'spark') + .append('g') + .attr('transform', 'translate(' + this.MARGIN.left + ',' + this.MARGIN.top + ')'); return this.svg; }; ContributorsAuthorGraph.prototype.draw_path = function(data) { - return this.svg.append("path").datum(data).attr("class", "area-contributor").attr("d", this.area); + return this.svg + .append('path') + .datum(data) + .attr('class', 'area-contributor') + .attr('d', this.area); }; ContributorsAuthorGraph.prototype.draw = function() { @@ -304,10 +379,10 @@ export const ContributorsAuthorGraph = (function(superClass) { ContributorsAuthorGraph.prototype.redraw = function() { this.set_domain(); - this.svg.select("path").datum(this.dates); - this.svg.select("path").attr("d", this.area); - this.svg.select(".x.axis").call(this.x_axis); - return this.svg.select(".y.axis").call(this.y_axis); + this.svg.select('path').datum(this.dates); + this.svg.select('path').attr('d', this.area); + this.svg.select('.x.axis').call(this.x_axis); + return this.svg.select('.y.axis').call(this.y_axis); }; return ContributorsAuthorGraph; diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js index cd0e2bc023c..988ae164955 100644 --- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js @@ -26,12 +26,12 @@ export default { by_author = _.toArray(by_author); return { total: total, - by_author: by_author + by_author: by_author, }; }, add_date: function(date, collection) { collection[date] = {}; - return collection[date].date = date; + return (collection[date].date = date); }, add_author: function(author, by_author, by_email) { var data, normalized_email; @@ -49,28 +49,28 @@ export default { return this.store_deletions(entry, total, by_author); }, store_commits: function(total, by_author) { - this.add(total, "commits", 1); - return this.add(by_author, "commits", 1); + this.add(total, 'commits', 1); + return this.add(by_author, 'commits', 1); }, add: function(collection, field, value) { if (collection[field] == null) { collection[field] = 0; } - return collection[field] += value; + return (collection[field] += value); }, store_additions: function(entry, total, by_author) { if (entry.additions == null) { entry.additions = 0; } - this.add(total, "additions", entry.additions); - return this.add(by_author, "additions", entry.additions); + this.add(total, 'additions', entry.additions); + return this.add(by_author, 'additions', entry.additions); }, store_deletions: function(entry, total, by_author) { if (entry.deletions == null) { entry.deletions = 0; } - this.add(total, "deletions", entry.deletions); - return this.add(by_author, "deletions", entry.deletions); + this.add(total, 'deletions', entry.deletions); + return this.add(by_author, 'deletions', entry.deletions); }, get_total_data: function(parsed_log, field) { var log, total_data; @@ -95,15 +95,18 @@ export default { } log = parsed_log.by_author; author_data = []; - _.each(log, (function(_this) { - return function(log_entry) { - var parsed_log_entry; - parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range); - if (!_.isEmpty(parsed_log_entry.dates)) { - return author_data.push(parsed_log_entry); - } - }; - })(this)); + _.each( + log, + (function(_this) { + return function(log_entry) { + var parsed_log_entry; + parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range); + if (!_.isEmpty(parsed_log_entry.dates)) { + return author_data.push(parsed_log_entry); + } + }; + })(this), + ); return _.sortBy(author_data, function(d) { return d[field]; }).reverse(); @@ -120,16 +123,19 @@ export default { parsed_entry.additions = 0; parsed_entry.deletions = 0; - _.each(_.omit(log_entry, 'author_name', 'author_email'), (function(_this) { - return function(value, key) { - if (_this.in_range(value.date, date_range)) { - parsed_entry.dates[value.date] = value[field]; - parsed_entry.commits += value.commits; - parsed_entry.additions += value.additions; - return parsed_entry.deletions += value.deletions; - } - }; - })(this)); + _.each( + _.omit(log_entry, 'author_name', 'author_email'), + (function(_this) { + return function(value, key) { + if (_this.in_range(value.date, date_range)) { + parsed_entry.dates[value.date] = value[field]; + parsed_entry.commits += value.commits; + parsed_entry.additions += value.additions; + return (parsed_entry.deletions += value.deletions); + } + }; + })(this), + ); return parsed_entry; }, in_range: function(date, date_range) { @@ -139,5 +145,5 @@ export default { } else { return false; } - } + }, }; diff --git a/app/assets/javascripts/pages/projects/init_blob.js b/app/assets/javascripts/pages/projects/init_blob.js index bc08ccf3584..bd8afa2d5ba 100644 --- a/app/assets/javascripts/pages/projects/init_blob.js +++ b/app/assets/javascripts/pages/projects/init_blob.js @@ -16,7 +16,8 @@ export default () => { ); const fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url'); - const fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href'); + const fileBlobPermalinkUrl = + fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href'); new ShortcutsNavigation(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/pages/projects/init_form.js b/app/assets/javascripts/pages/projects/init_form.js index 9f20a3e4e46..019efe077f7 100644 --- a/app/assets/javascripts/pages/projects/init_form.js +++ b/app/assets/javascripts/pages/projects/init_form.js @@ -1,7 +1,7 @@ import ZenMode from '~/zen_mode'; import GLForm from '~/gl_form'; -export default function ($formEl) { +export default function($formEl) { new ZenMode(); // eslint-disable-line no-new new GLForm($formEl); // eslint-disable-line no-new } diff --git a/app/assets/javascripts/pages/projects/issues/show.js b/app/assets/javascripts/pages/projects/issues/show.js index ef65196872c..8987c8e3f47 100644 --- a/app/assets/javascripts/pages/projects/issues/show.js +++ b/app/assets/javascripts/pages/projects/issues/show.js @@ -5,7 +5,7 @@ import ZenMode from '~/zen_mode'; import '~/notes/index'; import initIssueableApp from '~/issue_show'; -export default function () { +export default function() { initIssueableApp(); new Issue(); // eslint-disable-line no-new new ShortcutsIssuable(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/pages/projects/jobs/index/index.js b/app/assets/javascripts/pages/projects/jobs/index/index.js new file mode 100644 index 00000000000..1b57c67f16b --- /dev/null +++ b/app/assets/javascripts/pages/projects/jobs/index/index.js @@ -0,0 +1,16 @@ +import Vue from 'vue'; +import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; + +document.addEventListener('DOMContentLoaded', () => { + const remainingTimeElements = document.querySelectorAll('.js-remaining-time'); + remainingTimeElements.forEach( + el => + new Vue({ + ...GlCountdown, + el, + propsData: { + endDateString: el.dateTime, + }, + }), + ); +}); diff --git a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue index 5d2247f6c6d..e8b646f3f6e 100644 --- a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue +++ b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue @@ -1,72 +1,86 @@ <script> - import _ from 'underscore'; - import axios from '~/lib/utils/axios_utils'; - import createFlash from '~/flash'; - import GlModal from '~/vue_shared/components/gl_modal.vue'; - import { s__, sprintf } from '~/locale'; - import { visitUrl } from '~/lib/utils/url_utility'; - import eventHub from '../event_hub'; +import _ from 'underscore'; +import axios from '~/lib/utils/axios_utils'; +import createFlash from '~/flash'; +import GlModal from '~/vue_shared/components/gl_modal.vue'; +import { s__, sprintf } from '~/locale'; +import { visitUrl } from '~/lib/utils/url_utility'; +import eventHub from '../event_hub'; - export default { - components: { - GlModal, +export default { + components: { + GlModal, + }, + props: { + url: { + type: String, + required: true, }, - props: { - url: { - type: String, - required: true, - }, - labelTitle: { - type: String, - required: true, - }, - labelColor: { - type: String, - required: true, - }, - labelTextColor: { - type: String, - required: true, - }, - groupName: { - type: String, - required: true, - }, + labelTitle: { + type: String, + required: true, }, - computed: { - text() { - return sprintf(s__(`Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. - Existing project labels with the same title will be merged. This action cannot be reversed.`), { + labelColor: { + type: String, + required: true, + }, + labelTextColor: { + type: String, + required: true, + }, + groupName: { + type: String, + required: true, + }, + }, + computed: { + text() { + return sprintf( + s__(`Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. + Existing project labels with the same title will be merged. This action cannot be reversed.`), + { labelTitle: this.labelTitle, groupName: this.groupName, - }); - }, - title() { - const label = `<span + }, + ); + }, + title() { + const label = `<span class="label color-label" style="background-color: ${this.labelColor}; color: ${this.labelTextColor};" >${_.escape(this.labelTitle)}</span>`; - return sprintf(s__('Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>'), { + return sprintf( + s__('Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>'), + { labelTitle: label, - }, false); - }, + }, + false, + ); }, - methods: { - onSubmit() { - eventHub.$emit('promoteLabelModal.requestStarted', this.url); - return axios.post(this.url, { params: { format: 'json' } }) - .then((response) => { - eventHub.$emit('promoteLabelModal.requestFinished', { labelUrl: this.url, successful: true }); - visitUrl(response.data.url); - }) - .catch((error) => { - eventHub.$emit('promoteLabelModal.requestFinished', { labelUrl: this.url, successful: false }); - createFlash(error); + }, + methods: { + onSubmit() { + eventHub.$emit('promoteLabelModal.requestStarted', this.url); + return axios + .post(this.url, { params: { format: 'json' } }) + .then(response => { + eventHub.$emit('promoteLabelModal.requestFinished', { + labelUrl: this.url, + successful: true, + }); + visitUrl(response.data.url); + }) + .catch(error => { + eventHub.$emit('promoteLabelModal.requestFinished', { + labelUrl: this.url, + successful: false, }); - }, + createFlash(error); + }); }, - }; + }, +}; </script> <template> <gl-modal diff --git a/app/assets/javascripts/pages/projects/labels/index/index.js b/app/assets/javascripts/pages/projects/labels/index/index.js index 03cfef61311..36cf485f33d 100644 --- a/app/assets/javascripts/pages/projects/labels/index/index.js +++ b/app/assets/javascripts/pages/projects/labels/index/index.js @@ -10,20 +10,24 @@ const initLabelIndex = () => { initLabels(); const onRequestFinished = ({ labelUrl, successful }) => { - const button = document.querySelector(`.js-promote-project-label-button[data-url="${labelUrl}"]`); + const button = document.querySelector( + `.js-promote-project-label-button[data-url="${labelUrl}"]`, + ); if (!successful) { button.removeAttribute('disabled'); } }; - const onRequestStarted = (labelUrl) => { - const button = document.querySelector(`.js-promote-project-label-button[data-url="${labelUrl}"]`); + const onRequestStarted = labelUrl => { + const button = document.querySelector( + `.js-promote-project-label-button[data-url="${labelUrl}"]`, + ); button.setAttribute('disabled', ''); eventHub.$once('promoteLabelModal.requestFinished', onRequestFinished); }; - const onDeleteButtonClick = (event) => { + const onDeleteButtonClick = event => { const button = event.currentTarget; const modalProps = { labelTitle: button.dataset.labelTitle, @@ -37,12 +41,12 @@ const initLabelIndex = () => { }; const promoteLabelButtons = document.querySelectorAll('.js-promote-project-label-button'); - promoteLabelButtons.forEach((button) => { + promoteLabelButtons.forEach(button => { button.addEventListener('click', onDeleteButtonClick); }); eventHub.$once('promoteLabelModal.mounted', () => { - promoteLabelButtons.forEach((button) => { + promoteLabelButtons.forEach(button => { button.removeAttribute('disabled'); }); }); diff --git a/app/assets/javascripts/pages/projects/network/network.js b/app/assets/javascripts/pages/projects/network/network.js index 70fbb3f301c..226d63f05c4 100644 --- a/app/assets/javascripts/pages/projects/network/network.js +++ b/app/assets/javascripts/pages/projects/network/network.js @@ -6,13 +6,15 @@ import BranchGraph from '../../../network/branch_graph'; export default (function() { function Network(opts) { var vph; - $("#filter_ref").click(function() { - return $(this).closest('form').submit(); + $('#filter_ref').click(function() { + return $(this) + .closest('form') + .submit(); }); - this.branch_graph = new BranchGraph($(".network-graph"), opts); + this.branch_graph = new BranchGraph($('.network-graph'), opts); vph = $(window).height() - 250; $('.network-graph').css({ - 'height': vph + 'px' + height: vph + 'px', }); } diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js index 544360dcd51..6197dc8a9db 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js @@ -1,12 +1,16 @@ import Vue from 'vue'; import PipelineSchedulesCallout from '../shared/components/pipeline_schedules_callout.vue'; -document.addEventListener('DOMContentLoaded', () => new Vue({ - el: '#pipeline-schedules-callout', - components: { - 'pipeline-schedules-callout': PipelineSchedulesCallout, - }, - render(createElement) { - return createElement('pipeline-schedules-callout'); - }, -})); +document.addEventListener( + 'DOMContentLoaded', + () => + new Vue({ + el: '#pipeline-schedules-callout', + components: { + 'pipeline-schedules-callout': PipelineSchedulesCallout, + }, + render(createElement) { + return createElement('pipeline-schedules-callout'); + }, + }), +); diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue index ef53d67e7cb..ab6f42d928c 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue @@ -1,63 +1,63 @@ <script> - import _ from 'underscore'; +import _ from 'underscore'; - export default { - props: { - initialCronInterval: { - type: String, - required: false, - default: '', - }, - }, - data() { - return { - inputNameAttribute: 'schedule[cron]', - cronInterval: this.initialCronInterval, - cronIntervalPresets: { - everyDay: '0 4 * * *', - everyWeek: '0 4 * * 0', - everyMonth: '0 4 1 * *', - }, - cronSyntaxUrl: 'https://en.wikipedia.org/wiki/Cron', - customInputEnabled: false, - }; +export default { + props: { + initialCronInterval: { + type: String, + required: false, + default: '', }, - computed: { - intervalIsPreset() { - return _.contains(this.cronIntervalPresets, this.cronInterval); - }, - // The text input is editable when there's a custom interval, or when it's - // a preset interval and the user clicks the 'custom' radio button - isEditable() { - return !!(this.customInputEnabled || !this.intervalIsPreset); + }, + data() { + return { + inputNameAttribute: 'schedule[cron]', + cronInterval: this.initialCronInterval, + cronIntervalPresets: { + everyDay: '0 4 * * *', + everyWeek: '0 4 * * 0', + everyMonth: '0 4 1 * *', }, + cronSyntaxUrl: 'https://en.wikipedia.org/wiki/Cron', + customInputEnabled: false, + }; + }, + computed: { + intervalIsPreset() { + return _.contains(this.cronIntervalPresets, this.cronInterval); }, - watch: { - cronInterval() { - // updates field validation state when model changes, as - // glFieldError only updates on input. - this.$nextTick(() => { - gl.pipelineScheduleFieldErrors.updateFormValidityState(); - }); - }, + // The text input is editable when there's a custom interval, or when it's + // a preset interval and the user clicks the 'custom' radio button + isEditable() { + return !!(this.customInputEnabled || !this.intervalIsPreset); }, - 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(); + }); }, - methods: { - toggleCustomInput(shouldEnable) { - this.customInputEnabled = shouldEnable; + }, + created() { + if (this.intervalIsPreset) { + this.enableCustomInput = false; + } + }, + methods: { + toggleCustomInput(shouldEnable) { + this.customInputEnabled = shouldEnable; - if (shouldEnable) { - // We need to change the value so other radios don't remain selected - // because the model (cronInterval) hasn't changed. The server trims it. - this.cronInterval = `${this.cronInterval} `; - } - }, + if (shouldEnable) { + // We need to change the value so other radios don't remain selected + // because the model (cronInterval) hasn't changed. The server trims it. + this.cronInterval = `${this.cronInterval} `; + } }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue index 77508e62cef..33fc2420e4d 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue @@ -1,31 +1,31 @@ <script> - import Vue from 'vue'; - import Cookies from 'js-cookie'; - import Translate from '../../../../../vue_shared/translate'; - import illustrationSvg from '../icons/intro_illustration.svg'; +import Vue from 'vue'; +import Cookies from 'js-cookie'; +import Translate from '../../../../../vue_shared/translate'; +import illustrationSvg from '../icons/intro_illustration.svg'; - Vue.use(Translate); +Vue.use(Translate); - const cookieKey = 'pipeline_schedules_callout_dismissed'; +const cookieKey = 'pipeline_schedules_callout_dismissed'; - export default { - name: 'PipelineSchedulesCallout', - data() { - return { - docsUrl: document.getElementById('pipeline-schedules-callout').dataset.docsUrl, - calloutDismissed: Cookies.get(cookieKey) === 'true', - }; +export default { + name: 'PipelineSchedulesCallout', + data() { + return { + docsUrl: document.getElementById('pipeline-schedules-callout').dataset.docsUrl, + 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; - }, - methods: { - dismissCallout() { - this.calloutDismissed = true; - Cookies.set(cookieKey, this.calloutDismissed, { expires: 365 }); - }, - }, - }; + }, +}; </script> <template> <div diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js index 4ef0d11dd36..0057700c1b3 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js @@ -26,8 +26,7 @@ export default class TargetBranchDropdown { } formatBranchesList() { - return this.$dropdown.data('data') - .map(val => ({ name: val })); + return this.$dropdown.data('data').map(val => ({ name: val })); } setDropdownToggle() { diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js index c3ac54733a3..4d494efef6c 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js @@ -11,7 +11,9 @@ Vue.use(Translate); function initIntervalPatternInput() { const intervalPatternMount = document.getElementById('interval-pattern-input'); - const initialCronInterval = intervalPatternMount ? intervalPatternMount.dataset.initialInterval : ''; + const initialCronInterval = intervalPatternMount + ? intervalPatternMount.dataset.initialInterval + : ''; return new Vue({ el: intervalPatternMount, diff --git a/app/assets/javascripts/pages/projects/pipelines/charts/index.js b/app/assets/javascripts/pages/projects/pipelines/charts/index.js index 07b6992eba1..48353f3b4ef 100644 --- a/app/assets/javascripts/pages/projects/pipelines/charts/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/charts/index.js @@ -7,26 +7,29 @@ const options = { maintainAspectRatio: false, }; -const buildChart = (chartScope) => { +const buildChart = chartScope => { const data = { labels: chartScope.labels, - datasets: [{ - fillColor: '#707070', - strokeColor: '#707070', - pointColor: '#707070', - pointStrokeColor: '#EEE', - data: chartScope.totalValues, - }, - { - fillColor: '#1aaa55', - strokeColor: '#1aaa55', - pointColor: '#1aaa55', - pointStrokeColor: '#fff', - data: chartScope.successValues, - }, + datasets: [ + { + fillColor: '#707070', + strokeColor: '#707070', + pointColor: '#707070', + pointStrokeColor: '#EEE', + data: chartScope.totalValues, + }, + { + fillColor: '#1aaa55', + strokeColor: '#1aaa55', + pointColor: '#1aaa55', + pointStrokeColor: '#fff', + data: chartScope.successValues, + }, ], }; - const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d'); + const ctx = $(`#${chartScope.scope}Chart`) + .get(0) + .getContext('2d'); new Chart(ctx).Line(data, options); }; @@ -36,14 +39,16 @@ document.addEventListener('DOMContentLoaded', () => { const chartsData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML); const data = { labels: chartTimesData.labels, - datasets: [{ - fillColor: 'rgba(220,220,220,0.5)', - strokeColor: 'rgba(220,220,220,1)', - barStrokeWidth: 1, - barValueSpacing: 1, - barDatasetSpacing: 1, - data: chartTimesData.values, - }], + datasets: [ + { + fillColor: 'rgba(220,220,220,0.5)', + strokeColor: 'rgba(220,220,220,1)', + barStrokeWidth: 1, + barValueSpacing: 1, + barDatasetSpacing: 1, + data: chartTimesData.values, + }, + ], }; if (window.innerWidth < 768) { @@ -51,7 +56,11 @@ document.addEventListener('DOMContentLoaded', () => { options.scaleFontSize = 8; } - new Chart($('#build_timesChart').get(0).getContext('2d')).Bar(data, options); + new Chart( + $('#build_timesChart') + .get(0) + .getContext('2d'), + ).Bar(data, options); chartsData.forEach(scope => buildChart(scope)); }); diff --git a/app/assets/javascripts/pages/projects/pipelines/index/index.js b/app/assets/javascripts/pages/projects/pipelines/index/index.js index a84e2790680..fc337a7609b 100644 --- a/app/assets/javascripts/pages/projects/pipelines/index/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/index/index.js @@ -6,35 +6,39 @@ import { convertPermissionToBoolean } from '../../../../lib/utils/common_utils'; Vue.use(Translate); -document.addEventListener('DOMContentLoaded', () => new Vue({ - el: '#pipelines-list-vue', - components: { - pipelinesComponent, - }, - data() { - return { - store: new PipelinesStore(), - }; - }, - created() { - this.dataset = document.querySelector(this.$options.el).dataset; - }, - render(createElement) { - return createElement('pipelines-component', { - props: { - store: this.store, - endpoint: this.dataset.endpoint, - helpPagePath: this.dataset.helpPagePath, - emptyStateSvgPath: this.dataset.emptyStateSvgPath, - errorStateSvgPath: this.dataset.errorStateSvgPath, - noPipelinesSvgPath: this.dataset.noPipelinesSvgPath, - autoDevopsPath: this.dataset.helpAutoDevopsPath, - newPipelinePath: this.dataset.newPipelinePath, - canCreatePipeline: convertPermissionToBoolean(this.dataset.canCreatePipeline), - hasGitlabCi: convertPermissionToBoolean(this.dataset.hasGitlabCi), - ciLintPath: this.dataset.ciLintPath, - resetCachePath: this.dataset.resetCachePath, +document.addEventListener( + 'DOMContentLoaded', + () => + new Vue({ + el: '#pipelines-list-vue', + components: { + pipelinesComponent, }, - }); - }, -})); + data() { + return { + store: new PipelinesStore(), + }; + }, + created() { + this.dataset = document.querySelector(this.$options.el).dataset; + }, + render(createElement) { + return createElement('pipelines-component', { + props: { + store: this.store, + endpoint: this.dataset.endpoint, + helpPagePath: this.dataset.helpPagePath, + emptyStateSvgPath: this.dataset.emptyStateSvgPath, + errorStateSvgPath: this.dataset.errorStateSvgPath, + noPipelinesSvgPath: this.dataset.noPipelinesSvgPath, + autoDevopsPath: this.dataset.helpAutoDevopsPath, + newPipelinePath: this.dataset.newPipelinePath, + canCreatePipeline: convertPermissionToBoolean(this.dataset.canCreatePipeline), + hasGitlabCi: convertPermissionToBoolean(this.dataset.hasGitlabCi), + ciLintPath: this.dataset.ciLintPath, + resetCachePath: this.dataset.resetCachePath, + }, + }); + }, + }), +); diff --git a/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js index 94dfeb96e8c..ba4ae04ab3d 100644 --- a/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js +++ b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js @@ -2,9 +2,12 @@ import Pipelines from '~/pipelines'; export default () => { const { controllerAction } = document.querySelector('.js-pipeline-container').dataset; - const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`; + const pipelineStatusUrl = `${document + .querySelector('.js-pipeline-tab-link a') + .getAttribute('href')}/status.json`; - new Pipelines({ // eslint-disable-line no-new + // eslint-disable-next-line no-new + new Pipelines({ initTabs: true, pipelineStatusUrl, tabsOptions: { diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js index 52d66beefc9..a6bee49a6b1 100644 --- a/app/assets/javascripts/pages/projects/project.js +++ b/app/assets/javascripts/pages/projects/project.js @@ -64,7 +64,9 @@ export default class Project { const projectId = $(this).data('project-id'); const cookieKey = `hide_auto_devops_implicitly_enabled_banner_${projectId}`; Cookies.set(cookieKey, 'false'); - $(this).parents('.auto-devops-implicitly-enabled-banner').remove(); + $(this) + .parents('.auto-devops-implicitly-enabled-banner') + .remove(); return e.preventDefault(); }); Project.projectSelectDropdown(); diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js index 8f5ac3d8082..15c6fb550c1 100644 --- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js @@ -18,7 +18,7 @@ document.addEventListener('DOMContentLoaded', () => { // eslint-disable-next-line no-new new AjaxVariableList({ container: variableListEl, - saveButton: variableListEl.querySelector('.js-secret-variables-save-button'), + saveButton: variableListEl.querySelector('.js-ci-variables-save-button'), errorBox: variableListEl.querySelector('.js-ci-variable-error-box'), saveEndpoint: variableListEl.dataset.saveEndpoint, }); diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue index 06101290f6c..dced839c883 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue @@ -1,73 +1,71 @@ <script> - import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue'; +import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue'; - export default { - components: { - projectFeatureToggle, - }, +export default { + components: { + projectFeatureToggle, + }, - model: { - prop: 'value', - event: 'change', - }, + model: { + prop: 'value', + event: 'change', + }, - 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, - }, + 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, + }, + }, - computed: { - featureEnabled() { - return this.value !== 0; - }, + computed: { + featureEnabled() { + return this.value !== 0; + }, - displayOptions() { - if (this.featureEnabled) { - return this.options; - } - return [ - [0, 'Enable feature to choose access level'], - ]; - }, + displayOptions() { + if (this.featureEnabled) { + return this.options; + } + return [[0, 'Enable feature to choose access level']]; + }, - displaySelectInput() { - return this.disabledInput || !this.featureEnabled || this.displayOptions.length < 2; - }, + displaySelectInput() { + return this.disabledInput || !this.featureEnabled || this.displayOptions.length < 2; }, + }, - 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); - } - }, + 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)); - }, + selectOption(e) { + this.$emit('change', Number(e.target.value)); }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue index 83437363af5..898d605463f 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue @@ -1,23 +1,23 @@ <script> - export default { - props: { - label: { - type: String, - required: false, - default: null, - }, - helpPath: { - type: String, - required: false, - default: null, - }, - helpText: { - 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, + }, + }, +}; </script> <template> diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue index a16f7e6b77c..c0ec7a5dc94 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue @@ -1,202 +1,200 @@ <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 { - components: { - projectFeatureSetting, - projectFeatureToggle, - projectSettingRow, - }, +export default { + components: { + projectFeatureSetting, + projectFeatureToggle, + projectSettingRow, + }, - 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, - default: '', - }, - lfsHelpPath: { - type: String, - required: false, - default: '', - }, - registryHelpPath: { - type: String, - required: false, - default: '', - }, - pagesAvailable: { - type: Boolean, - required: false, - default: false, - }, - pagesAccessControlEnabled: { - type: Boolean, - required: false, - default: false, - }, - pagesHelpPath: { - type: String, - required: false, - default: '', - }, + 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, + default: '', }, + lfsHelpPath: { + type: String, + required: false, + default: '', + }, + registryHelpPath: { + type: String, + required: false, + default: '', + }, + pagesAvailable: { + type: Boolean, + required: false, + default: false, + }, + pagesAccessControlEnabled: { + type: Boolean, + required: false, + default: false, + }, + pagesHelpPath: { + type: String, + required: false, + default: '', + }, + }, - data() { - const defaults = { - visibilityOptions, - visibilityLevel: visibilityOptions.PUBLIC, - issuesAccessLevel: 20, - repositoryAccessLevel: 20, - mergeRequestsAccessLevel: 20, - buildsAccessLevel: 20, - wikiAccessLevel: 20, - snippetsAccessLevel: 20, - pagesAccessLevel: 20, - containerRegistryEnabled: true, - lfsEnabled: true, - requestAccessEnabled: true, - highlightChangesClass: false, - }; + data() { + const defaults = { + visibilityOptions, + visibilityLevel: visibilityOptions.PUBLIC, + issuesAccessLevel: 20, + repositoryAccessLevel: 20, + mergeRequestsAccessLevel: 20, + buildsAccessLevel: 20, + wikiAccessLevel: 20, + snippetsAccessLevel: 20, + pagesAccessLevel: 20, + containerRegistryEnabled: true, + lfsEnabled: true, + requestAccessEnabled: true, + highlightChangesClass: false, + }; - return { ...defaults, ...this.currentSettings }; - }, + return { ...defaults, ...this.currentSettings }; + }, - computed: { - featureAccessLevelOptions() { - const options = [ - [10, 'Only Project Members'], - ]; - if (this.visibilityLevel !== visibilityOptions.PRIVATE) { - options.push([20, 'Everyone With Access']); - } - return options; - }, + computed: { + featureAccessLevelOptions() { + const options = [[10, 'Only Project Members']]; + if (this.visibilityLevel !== visibilityOptions.PRIVATE) { + options.push([20, 'Everyone With Access']); + } + return options; + }, - repoFeatureAccessLevelOptions() { - return this.featureAccessLevelOptions.filter( - ([value]) => value <= this.repositoryAccessLevel, - ); - }, + repoFeatureAccessLevelOptions() { + return this.featureAccessLevelOptions.filter( + ([value]) => value <= this.repositoryAccessLevel, + ); + }, - pagesFeatureAccessLevelOptions() { - if (this.visibilityLevel !== visibilityOptions.PUBLIC) { - return this.featureAccessLevelOptions.concat([[30, 'Everyone']]); - } - return this.featureAccessLevelOptions; - }, + pagesFeatureAccessLevelOptions() { + if (this.visibilityLevel !== visibilityOptions.PUBLIC) { + return this.featureAccessLevelOptions.concat([[30, 'Everyone']]); + } + return this.featureAccessLevelOptions; + }, - repositoryEnabled() { - return this.repositoryAccessLevel > 0; - }, + repositoryEnabled() { + return this.repositoryAccessLevel > 0; + }, - visibilityLevelDescription() { - return visibilityLevelDescriptions[this.visibilityLevel]; - }, + visibilityLevelDescription() { + return visibilityLevelDescriptions[this.visibilityLevel]; }, + }, - 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); - if (this.pagesAccessLevel === 20) { - // When from Internal->Private narrow access for only members - this.pagesAccessLevel = 10; - } - 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; - if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20; - this.highlightChanges(); + 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); + if (this.pagesAccessLevel === 20) { + // When from Internal->Private narrow access for only members + this.pagesAccessLevel = 10; } - }, + 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; + if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 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); + 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; - } - } else if (oldValue === 0) { - this.mergeRequestsAccessLevel = value; - this.buildsAccessLevel = value; - this.containerRegistryEnabled = true; - this.lfsEnabled = true; + 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; + } + }, - 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; - }); - }, + methods: { + highlightChanges() { + this.highlightChangesClass = true; + this.$nextTick(() => { + this.highlightChangesClass = false; + }); + }, - visibilityAllowed(option) { - return this.allowedVisibilityOptions.includes(option); - }, + visibilityAllowed(option) { + return this.allowedVisibilityOptions.includes(option); }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/pages/projects/shared/permissions/constants.js b/app/assets/javascripts/pages/projects/shared/permissions/constants.js index ce47562f259..bc5c29d12b5 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/constants.js +++ b/app/assets/javascripts/pages/projects/shared/permissions/constants.js @@ -5,7 +5,9 @@ export const visibilityOptions = { }; export const visibilityLevelDescriptions = { - [visibilityOptions.PRIVATE]: 'The project is accessible only by members of the project. Access must be granted explicitly to each user.', + [visibilityOptions.PRIVATE]: + 'The project is accessible only by members of the project. Access must be granted explicitly to each user.', [visibilityOptions.INTERNAL]: 'The project can be accessed by any user who is logged in.', - [visibilityOptions.PUBLIC]: 'The project can be accessed by anyone, regardless of authentication.', + [visibilityOptions.PUBLIC]: + 'The project can be accessed by anyone, regardless of authentication.', }; diff --git a/app/assets/javascripts/pages/projects/shared/project_avatar.js b/app/assets/javascripts/pages/projects/shared/project_avatar.js index 447877752fe..1e69ecb481d 100644 --- a/app/assets/javascripts/pages/projects/shared/project_avatar.js +++ b/app/assets/javascripts/pages/projects/shared/project_avatar.js @@ -8,8 +8,9 @@ export default function projectAvatar() { $('.js-project-avatar-input').bind('change', function onClickAvatarInput() { const form = $(this).closest('form'); - // eslint-disable-next-line no-useless-escape - const filename = $(this).val().replace(/^.*[\\\/]/, ''); + const filename = $(this) + .val() + .replace(/^.*[\\\/]/, ''); // eslint-disable-line no-useless-escape return form.find('.js-avatar-filename').text(filename); }); } diff --git a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue index 75cb6374ad5..f970a5ebb64 100644 --- a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue +++ b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue @@ -1,8 +1,15 @@ <script> import _ from 'underscore'; import { s__, sprintf } from '~/locale'; +import { GlModal, GlModalDirective } from '@gitlab-org/gitlab-ui'; export default { + components: { + GlModal, + }, + directives: { + 'gl-modal': GlModalDirective, + }, props: { deleteWikiUrl: { type: String, @@ -54,7 +61,7 @@ export default { > {{ __('Delete') }} </button> - <gl-ui-modal + <gl-modal :title="title" :ok-title="s__('WikiPageConfirmDelete|Delete page')" :modal-id="modalId" @@ -81,6 +88,6 @@ export default { name="authenticity_token" /> </form> - </gl-ui-modal> + </gl-modal> </div> </template> diff --git a/app/assets/javascripts/pages/projects/wikis/index.js b/app/assets/javascripts/pages/projects/wikis/index.js index c2629090f01..f5fd84d69ac 100644 --- a/app/assets/javascripts/pages/projects/wikis/index.js +++ b/app/assets/javascripts/pages/projects/wikis/index.js @@ -21,7 +21,8 @@ document.addEventListener('DOMContentLoaded', () => { const { deleteWikiUrl, pageTitle } = deleteWikiModalWrapperEl.dataset; - new Vue({ // eslint-disable-line no-new + // eslint-disable-next-line no-new + new Vue({ el: deleteWikiModalWrapperEl, data: { deleteWikiUrl: '', diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js index e3e0ab91993..0c896c8599e 100644 --- a/app/assets/javascripts/pages/search/show/search.js +++ b/app/assets/javascripts/pages/search/show/search.js @@ -22,7 +22,7 @@ export default class Search { fields: ['full_name'], }, data(term, callback) { - return Api.groups(term, {}, (data) => { + return Api.groups(term, {}, data => { data.unshift({ full_name: 'Any', }); @@ -37,7 +37,7 @@ export default class Search { return obj.full_name; }, toggleLabel(obj) { - return `${($groupDropdown.data('defaultLabel'))} ${obj.full_name}`; + return `${$groupDropdown.data('defaultLabel')} ${obj.full_name}`; }, clicked: () => Search.submitSearch(), }); @@ -52,7 +52,7 @@ export default class Search { }, data: (term, callback) => { this.getProjectsData(term) - .then((data) => { + .then(data => { data.unshift({ name_with_namespace: 'Any', }); @@ -70,7 +70,7 @@ export default class Search { return obj.name_with_namespace; }, toggleLabel(obj) { - return `${($projectDropdown.data('defaultLabel'))} ${obj.name_with_namespace}`; + return `${$projectDropdown.data('defaultLabel')} ${obj.name_with_namespace}`; }, clicked: () => Search.submitSearch(), }); @@ -99,17 +99,24 @@ export default class Search { } clearSearchField() { - return $(this.searchInput).val('').trigger('keyup').focus(); + return $(this.searchInput) + .val('') + .trigger('keyup') + .focus(); } getProjectsData(term) { - return new Promise((resolve) => { + return new Promise(resolve => { if (this.groupId) { Api.groupProjects(this.groupId, term, {}, resolve); } else { - Api.projects(term, { - order_by: 'id', - }, resolve); + Api.projects( + term, + { + order_by: 'id', + }, + resolve, + ); } }); } diff --git a/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js b/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js index 1e7c29aefaa..2b8f1e8b0ef 100644 --- a/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js +++ b/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js @@ -20,7 +20,7 @@ export default class SigninTabsMemoizer { bootstrap() { const tabs = document.querySelectorAll(this.tabSelector); if (tabs.length > 0) { - tabs[0].addEventListener('click', (e) => { + tabs[0].addEventListener('click', e => { if (e.target && e.target.nodeName === 'A') { const anchorName = e.target.getAttribute('href'); this.saveData(anchorName); diff --git a/app/assets/javascripts/pages/sessions/new/username_validator.js b/app/assets/javascripts/pages/sessions/new/username_validator.js index d621f988d86..7a41805bada 100644 --- a/app/assets/javascripts/pages/sessions/new/username_validator.js +++ b/app/assets/javascripts/pages/sessions/new/username_validator.js @@ -22,10 +22,10 @@ export default class UsernameValidator { available: false, valid: false, pending: false, - empty: true + empty: true, }; - const debounceTimeout = _.debounce((username) => { + const debounceTimeout = _.debounce(username => { this.validateUsername(username); }, debounceTimeoutDuration); @@ -81,7 +81,8 @@ export default class UsernameValidator { this.state.pending = true; this.state.available = false; this.renderState(); - axios.get(`${gon.relative_url_root}/users/${username}/exists`) + axios + .get(`${gon.relative_url_root}/users/${username}/exists`) .then(({ data }) => this.setAvailabilityState(data.exists)) .catch(() => flash(__('An error occurred while validating username'))); } @@ -100,8 +101,7 @@ export default class UsernameValidator { clearFieldValidationState() { this.inputElement.siblings('p').hide(); - this.inputElement.removeClass(invalidInputClass) - .removeClass(successInputClass); + this.inputElement.removeClass(invalidInputClass).removeClass(successInputClass); } setUnavailableState() { diff --git a/app/assets/javascripts/pages/users/index.js b/app/assets/javascripts/pages/users/index.js index 6b1626b0161..a191df00dfa 100644 --- a/app/assets/javascripts/pages/users/index.js +++ b/app/assets/javascripts/pages/users/index.js @@ -13,10 +13,12 @@ function initUserProfile(action) { new UserTabs({ parentEl: '.user-profile', action }); // hide project limit message - $('.hide-project-limit-message').on('click', (e) => { + $('.hide-project-limit-message').on('click', e => { e.preventDefault(); Cookies.set('hide_project_limit_message', 'false'); - $(this).parents('.project-limit-message').remove(); + $(this) + .parents('.project-limit-message') + .remove(); }); } diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index 1de9945baad..04bcb16f036 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -170,7 +170,7 @@ export default class UserTabs { this.loadActivityCalendar('activity'); // eslint-disable-next-line no-new - new Activities(); + new Activities('#activity'); this.loaded.activity = true; } diff --git a/app/assets/javascripts/performance_bar/components/simple_metric.vue b/app/assets/javascripts/performance_bar/components/simple_metric.vue index 760ea8fe1e6..7a558558c4d 100644 --- a/app/assets/javascripts/performance_bar/components/simple_metric.vue +++ b/app/assets/javascripts/performance_bar/components/simple_metric.vue @@ -1,29 +1,29 @@ <script> - export default { - props: { - currentRequest: { - type: Object, - required: true, - }, - metric: { - type: String, - required: true, - }, +export default { + props: { + currentRequest: { + type: Object, + required: true, }, - computed: { - duration() { - return ( - this.currentRequest.details[this.metric] && - this.currentRequest.details[this.metric].duration - ); - }, - calls() { - return ( - this.currentRequest.details[this.metric] && this.currentRequest.details[this.metric].calls - ); - }, + metric: { + type: String, + required: true, }, - }; + }, + computed: { + duration() { + return ( + this.currentRequest.details[this.metric] && + this.currentRequest.details[this.metric].duration + ); + }, + calls() { + return ( + this.currentRequest.details[this.metric] && this.currentRequest.details[this.metric].calls + ); + }, + }, +}; </script> <template> <div diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js index 6e5ef0ac0b2..29bfb7ee5df 100644 --- a/app/assets/javascripts/performance_bar/index.js +++ b/app/assets/javascripts/performance_bar/index.js @@ -9,8 +9,7 @@ export default ({ container }) => performanceBarApp: () => import('./components/performance_bar_app.vue'), }, data() { - const performanceBarData = document.querySelector(this.$options.el) - .dataset; + const performanceBarData = document.querySelector(this.$options.el).dataset; const store = new PerformanceBarStore(); return { diff --git a/app/assets/javascripts/performance_bar/services/performance_bar_service.js b/app/assets/javascripts/performance_bar/services/performance_bar_service.js index 60d9ba62570..3a496fa2ed8 100644 --- a/app/assets/javascripts/performance_bar/services/performance_bar_service.js +++ b/app/assets/javascripts/performance_bar/services/performance_bar_service.js @@ -11,8 +11,10 @@ export default class PerformanceBarService { static registerInterceptor(peekUrl, callback) { const interceptor = response => { - const [fireCallback, requestId, requestUrl] = - PerformanceBarService.callbackParams(response, peekUrl); + const [fireCallback, requestId, requestUrl] = PerformanceBarService.callbackParams( + response, + peekUrl, + ); if (fireCallback) { callback(requestId, requestUrl); @@ -30,10 +32,7 @@ export default class PerformanceBarService { static removeInterceptor(interceptor) { axios.interceptors.response.eject(interceptor); - Vue.http.interceptors = _.without( - Vue.http.interceptors, - vueResourceInterceptor, - ); + Vue.http.interceptors = _.without(Vue.http.interceptors, vueResourceInterceptor); } static callbackParams(response, peekUrl) { diff --git a/app/assets/javascripts/performance_bar/stores/performance_bar_store.js b/app/assets/javascripts/performance_bar/stores/performance_bar_store.js index c6b2f55243c..031e774d533 100644 --- a/app/assets/javascripts/performance_bar/stores/performance_bar_store.js +++ b/app/assets/javascripts/performance_bar/stores/performance_bar_store.js @@ -32,8 +32,6 @@ export default class PerformanceBarStore { } canTrackRequest(requestUrl) { - return ( - this.requests.filter(request => request.url === requestUrl).length < 2 - ); + return this.requests.filter(request => request.url === requestUrl).length < 2; } } diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue index 16e69759091..a7507fb3b6f 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue @@ -1,16 +1,17 @@ <script> import { s__, sprintf } from '~/locale'; -import { formatTime } from '~/lib/utils/datetime_utility'; import eventHub from '../event_hub'; -import icon from '../../vue_shared/components/icon.vue'; +import Icon from '../../vue_shared/components/icon.vue'; import tooltip from '../../vue_shared/directives/tooltip'; +import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; export default { directives: { tooltip, }, components: { - icon, + Icon, + GlCountdown, }, props: { actions: { @@ -51,11 +52,6 @@ export default { return !action.playable; }, - - remainingTime(action) { - const remainingMilliseconds = new Date(action.scheduled_at).getTime() - Date.now(); - return formatTime(Math.max(0, remainingMilliseconds)); - }, }, }; </script> @@ -100,7 +96,7 @@ export default { class="pull-right" > <icon name="clock" /> - {{ remainingTime(action) }} + <gl-countdown :end-date-string="action.scheduled_at" /> </span> </button> </li> diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue index d40de95e051..e0f0434e03d 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue @@ -1,13 +1,13 @@ <script> import tooltip from '../../vue_shared/directives/tooltip'; -import icon from '../../vue_shared/components/icon.vue'; +import Icon from '../../vue_shared/components/icon.vue'; export default { directives: { tooltip, }, components: { - icon, + Icon, }, props: { artifacts: { diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue index cb14d4400d1..3339b5c13ed 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue @@ -88,25 +88,25 @@ export default { class="table-section section-10 js-pipeline-status pipeline-status" role="rowheader" > - Status + {{ s__('Pipeline|Status') }} </div> <div class="table-section section-15 js-pipeline-info pipeline-info" role="rowheader" > - Pipeline + {{ s__('Pipeline|Pipeline') }} </div> <div class="table-section section-20 js-pipeline-commit pipeline-commit" role="rowheader" > - Commit + {{ s__('Pipeline|Commit') }} </div> <div class="table-section section-20 js-pipeline-stages pipeline-stages" role="rowheader" > - Stages + {{ s__('Pipeline|Stages') }} </div> </div> <pipelines-table-row-component diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue index 4f89ee66023..026d533d10f 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue @@ -261,7 +261,7 @@ export default { class="table-mobile-header" role="rowheader" > - Status + {{ s__('Pipeline|Status') }} </div> <div class="table-mobile-content"> <ci-badge @@ -279,8 +279,9 @@ export default { <div class="table-section section-20"> <div class="table-mobile-header" - role="rowheader"> - Commit + role="rowheader" + > + {{ s__('Pipeline|Commit') }} </div> <div class="table-mobile-content"> <commit-component @@ -298,8 +299,9 @@ export default { <div class="table-section section-wrap section-20 stage-cell"> <div class="table-mobile-header" - role="rowheader"> - Stages + role="rowheader" + > + {{ s__('Pipeline|Stages') }} </div> <div class="table-mobile-content"> <template v-if="pipeline.details.stages.length > 0"> diff --git a/app/assets/javascripts/pipelines/components/time_ago.vue b/app/assets/javascripts/pipelines/components/time_ago.vue index cd43d78de40..bed690200b8 100644 --- a/app/assets/javascripts/pipelines/components/time_ago.vue +++ b/app/assets/javascripts/pipelines/components/time_ago.vue @@ -60,7 +60,7 @@ export default { class="table-mobile-header" role="rowheader" > - Duration + {{ s__('Pipeline|Duration') }} </div> <div class="table-mobile-content"> <p @@ -87,7 +87,8 @@ export default { v-tooltip :title="tooltipTitle(finishedTime)" data-placement="top" - data-container="body"> + data-container="body" + > {{ timeFormated(finishedTime) }} </time> </p> 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 974629fa2af..99b57f4c9d5 100644 --- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue +++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue @@ -1,78 +1,78 @@ <script> - import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; - import { __, s__, sprintf } from '~/locale'; - import csrf from '~/lib/utils/csrf'; +import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; +import { __, s__, sprintf } from '~/locale'; +import csrf from '~/lib/utils/csrf'; - export default { - components: { - DeprecatedModal, +export default { + components: { + DeprecatedModal, + }, + props: { + actionUrl: { + type: String, + required: true, }, - props: { - actionUrl: { - type: String, - required: true, - }, - confirmWithPassword: { - type: Boolean, - required: true, - }, - username: { - type: String, - required: true, - }, + confirmWithPassword: { + type: Boolean, + required: true, }, - data() { - return { - enteredPassword: '', - enteredUsername: '', - }; + username: { + type: String, + required: true, }, - computed: { - csrfToken() { - return csrf.token; - }, - inputLabel() { - let confirmationValue; - if (this.confirmWithPassword) { - confirmationValue = __('password'); - } else { - confirmationValue = __('username'); - } + }, + data() { + return { + enteredPassword: '', + enteredUsername: '', + }; + }, + computed: { + csrfToken() { + return csrf.token; + }, + inputLabel() { + let confirmationValue; + if (this.confirmWithPassword) { + confirmationValue = __('password'); + } else { + confirmationValue = __('username'); + } - confirmationValue = `<code>${confirmationValue}</code>`; + confirmationValue = `<code>${confirmationValue}</code>`; - return sprintf( - s__('Profiles|Type your %{confirmationValue} to confirm:'), - { confirmationValue }, - false, - ); - }, - text() { - return sprintf( - s__(`Profiles| + return sprintf( + s__('Profiles|Type your %{confirmationValue} to confirm:'), + { confirmationValue }, + false, + ); + }, + text() { + return sprintf( + s__(`Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), - { - yourAccount: `<strong>${s__('Profiles|your account')}</strong>`, - deleteAccount: `<strong>${s__('Profiles|Delete Account')}</strong>`, - }, - false, - ); - }, + { + yourAccount: `<strong>${s__('Profiles|your account')}</strong>`, + deleteAccount: `<strong>${s__('Profiles|Delete Account')}</strong>`, + }, + false, + ); }, - methods: { - canSubmit() { - if (this.confirmWithPassword) { - return this.enteredPassword !== ''; - } + }, + methods: { + canSubmit() { + if (this.confirmWithPassword) { + return this.enteredPassword !== ''; + } - return this.enteredUsername === this.username; - }, - onSubmit() { - this.$refs.form.submit(); - }, + return this.enteredUsername === this.username; + }, + onSubmit() { + this.$refs.form.submit(); }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js index af134881f31..befe91c332f 100644 --- a/app/assets/javascripts/profile/gl_crop.js +++ b/app/assets/javascripts/profile/gl_crop.js @@ -4,20 +4,35 @@ import $ from 'jquery'; import 'cropper'; import _ from 'underscore'; -((global) => { +(global => { // Matches everything but the file name const FILENAMEREGEX = /^.*[\\\/]/; class GitLabCrop { - constructor(input, { filename, previewImage, modalCrop, pickImageEl, uploadImageBtn, modalCropImg, - exportWidth = 200, exportHeight = 200, cropBoxWidth = 200, cropBoxHeight = 200 } = {}) { + constructor( + input, + { + filename, + previewImage, + modalCrop, + pickImageEl, + uploadImageBtn, + modalCropImg, + exportWidth = 200, + exportHeight = 200, + cropBoxWidth = 200, + cropBoxHeight = 200, + } = {}, + ) { this.onUploadImageBtnClick = this.onUploadImageBtnClick.bind(this); this.onModalHide = this.onModalHide.bind(this); this.onModalShow = this.onModalShow.bind(this); this.onPickImageClick = this.onPickImageClick.bind(this); this.fileInput = $(input); this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg; - this.fileInput.attr('name', `${this.fileInput.attr('name')}-trigger`).attr('id', `${this.fileInput.attr('id')}-trigger`); + this.fileInput + .attr('name', `${this.fileInput.attr('name')}-trigger`) + .attr('id', `${this.fileInput.attr('id')}-trigger`); this.exportWidth = exportWidth; this.exportHeight = exportHeight; this.cropBoxWidth = cropBoxWidth; @@ -59,7 +74,7 @@ import _ from 'underscore'; btn = this; return _this.onActionBtnClick(btn); }); - return this.croppedImageBlob = null; + return (this.croppedImageBlob = null); } onPickImageClick() { @@ -94,9 +109,9 @@ import _ from 'underscore'; width: cropBoxWidth, height: cropBoxHeight, left: (container.width - cropBoxWidth) / 2, - top: (container.height - cropBoxHeight) / 2 + top: (container.height - cropBoxHeight) / 2, }); - } + }, }); } @@ -116,7 +131,7 @@ import _ from 'underscore'; var data, result; data = $(btn).data(); if (this.modalCropImg.data('cropper') && data.method) { - return result = this.modalCropImg.cropper(data.method, data.option); + return (result = this.modalCropImg.cropper(data.method, data.option)); } } @@ -127,7 +142,7 @@ import _ from 'underscore'; readFile(input) { var _this, reader; _this = this; - reader = new FileReader; + reader = new FileReader(); reader.onload = () => { _this.modalCropImg.attr('src', reader.result); return _this.modalCrop.modal('show'); @@ -145,7 +160,7 @@ import _ from 'underscore'; array.push(binary.charCodeAt(i)); } return new Blob([new Uint8Array(array)], { - type: 'image/png' + type: 'image/png', }); } @@ -157,11 +172,13 @@ import _ from 'underscore'; } setBlob() { - this.dataURL = this.modalCropImg.cropper('getCroppedCanvas', { - width: 200, - height: 200 - }).toDataURL('image/png'); - return this.croppedImageBlob = this.dataURLtoBlob(this.dataURL); + this.dataURL = this.modalCropImg + .cropper('getCroppedCanvas', { + width: 200, + height: 200, + }) + .toDataURL('image/png'); + return (this.croppedImageBlob = this.dataURLtoBlob(this.dataURL)); } getBlob() { diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js index e49c67ffb5c..8704a655b28 100644 --- a/app/assets/javascripts/profile/profile.js +++ b/app/assets/javascripts/profile/profile.js @@ -26,11 +26,7 @@ export default class Profile { } bindEvents() { - $('.js-preferences-form').on( - 'change.preference', - 'input[type=radio]', - this.submitForm, - ); + $('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm); $('#user_notification_email').on('change', this.submitForm); $('#user_notified_of_own_activity').on('change', this.submitForm); this.form.on('submit', this.onSubmitForm); diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index ebe18b47e4e..998554d1be5 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -4,8 +4,10 @@ import { slugifyWithHyphens } from '../lib/utils/text_utility'; let hasUserDefinedProjectPath = false; -const deriveProjectPathFromUrl = ($projectImportUrl) => { - const $currentProjectPath = $projectImportUrl.parents('.toggle-import-form').find('#project_path'); +const deriveProjectPathFromUrl = $projectImportUrl => { + const $currentProjectPath = $projectImportUrl + .parents('.toggle-import-form') + .find('#project_path'); if (hasUserDefinedProjectPath) { return; } @@ -52,9 +54,11 @@ const bindEvents = () => { return; } - $('.how_to_import_link').on('click', (e) => { + $('.how_to_import_link').on('click', e => { e.preventDefault(); - $(e.currentTarget).next('.modal').show(); + $(e.currentTarget) + .next('.modal') + .show(); }); $('.modal-header .close').on('click', () => { @@ -63,15 +67,21 @@ const bindEvents = () => { $('.btn_import_gitlab_project').on('click', () => { const importHref = $('a.btn_import_gitlab_project').attr('href'); - $('.btn_import_gitlab_project') - .attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&name=${$projectName.val()}&path=${$projectPath.val()}`); + $('.btn_import_gitlab_project').attr( + 'href', + `${importHref}?namespace_id=${$( + '#project_namespace_id', + ).val()}&name=${$projectName.val()}&path=${$projectPath.val()}`, + ); }); if ($pushNewProjectTipTrigger) { $pushNewProjectTipTrigger .removeAttr('rel') .removeAttr('target') - .on('click', (e) => { e.preventDefault(); }) + .on('click', e => { + e.preventDefault(); + }) .popover({ title: $pushNewProjectTipTrigger.data('title'), placement: 'bottom', @@ -79,13 +89,15 @@ const bindEvents = () => { content: $('.push-new-project-tip-template').html(), }) .on('shown.bs.popover', () => { - $(document).on('click.popover touchstart.popover', (event) => { + $(document).on('click.popover touchstart.popover', event => { if ($(event.target).closest('.popover').length === 0) { $pushNewProjectTipTrigger.trigger('click'); } }); - const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find('.js-select-on-focus'); + const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find( + '.js-select-on-focus', + ); addSelectOnFocusBehaviour(target); target.focus(); @@ -117,16 +129,18 @@ const bindEvents = () => { const selectedTemplate = templates[value]; $selectedTemplateText.text(selectedTemplate.text); - $(selectedTemplate.icon).clone().addClass('d-block').appendTo($selectedIcon); + $(selectedTemplate.icon) + .clone() + .addClass('d-block') + .appendTo($selectedIcon); const $activeTabProjectName = $('.tab-pane.active #project_name'); const $activeTabProjectPath = $('.tab-pane.active #project_path'); $activeTabProjectName.focus(); - $activeTabProjectName - .keyup(() => { - onProjectNameChange($activeTabProjectName, $activeTabProjectPath); - hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0; - }); + $activeTabProjectName.keyup(() => { + onProjectNameChange($activeTabProjectName, $activeTabProjectPath); + hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0; + }); } $useTemplateBtn.on('change', chooseTemplate); diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js index b601b19e7be..48343c8ba0a 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_create.js +++ b/app/assets/javascripts/protected_branches/protected_branch_create.js @@ -46,8 +46,12 @@ export default class ProtectedBranchCreate { onSelect() { // Enable submit button const $branchInput = this.$form.find('input[name="protected_branch[name]"]'); - const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]'); - const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]'); + const $allowedToMergeInput = this.$form.find( + 'input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]', + ); + const $allowedToPushInput = this.$form.find( + 'input[name="protected_branch[push_access_levels_attributes][0][access_level]"]', + ); const completedForm = !( $branchInput.val() && $allowedToMergeInput.length && diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js index 54560d08ad7..5bc08f60d16 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit.js +++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js @@ -29,8 +29,12 @@ export default class ProtectedBranchEdit { } onSelect() { - const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`); - const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`); + const $allowedToMergeInput = this.$wrap.find( + `input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`, + ); + const $allowedToPushInput = this.$wrap.find( + `input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`, + ); // Do not update if one dropdown has not selected any option if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; @@ -38,25 +42,36 @@ export default class ProtectedBranchEdit { this.$allowedToMergeDropdown.disable(); this.$allowedToPushDropdown.disable(); - axios.patch(this.$wrap.data('url'), { - protected_branch: { - merge_access_levels_attributes: [{ - id: this.$allowedToMergeDropdown.data('accessLevelId'), - access_level: $allowedToMergeInput.val(), - }], - push_access_levels_attributes: [{ - id: this.$allowedToPushDropdown.data('accessLevelId'), - access_level: $allowedToPushInput.val(), - }], - }, - }).then(() => { - this.$allowedToMergeDropdown.enable(); - this.$allowedToPushDropdown.enable(); - }).catch(() => { - this.$allowedToMergeDropdown.enable(); - this.$allowedToPushDropdown.enable(); - - flash('Failed to update branch!', 'alert', document.querySelector('.js-protected-branches-list')); - }); + axios + .patch(this.$wrap.data('url'), { + protected_branch: { + merge_access_levels_attributes: [ + { + id: this.$allowedToMergeDropdown.data('accessLevelId'), + access_level: $allowedToMergeInput.val(), + }, + ], + push_access_levels_attributes: [ + { + id: this.$allowedToPushDropdown.data('accessLevelId'), + access_level: $allowedToPushInput.val(), + }, + ], + }, + }) + .then(() => { + this.$allowedToMergeDropdown.enable(); + this.$allowedToPushDropdown.enable(); + }) + .catch(() => { + this.$allowedToMergeDropdown.enable(); + this.$allowedToPushDropdown.enable(); + + flash( + 'Failed to update branch!', + 'alert', + document.querySelector('.js-protected-branches-list'), + ); + }); } } diff --git a/app/assets/javascripts/protected_tags/protected_tag_create.js b/app/assets/javascripts/protected_tags/protected_tag_create.js index 2f8116df0d2..fddf2674cbb 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_create.js +++ b/app/assets/javascripts/protected_tags/protected_tag_create.js @@ -40,7 +40,9 @@ export default class ProtectedTagCreate { const $tagInput = this.$form.find('input[name="protected_tag[name]"]'); const $allowedToCreateInput = this.$form.find('#create_access_levels_attributes'); - this.$form.find('input[type="submit"]').prop('disabled', !($tagInput.val() && $allowedToCreateInput.length)); + this.$form + .find('input[type="submit"]') + .prop('disabled', !($tagInput.val() && $allowedToCreateInput.length)); } static getProtectedTags(term, callback) { diff --git a/app/assets/javascripts/protected_tags/protected_tag_edit.js b/app/assets/javascripts/protected_tags/protected_tag_edit.js index 8687b2a4044..c52497e62f2 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_edit.js +++ b/app/assets/javascripts/protected_tags/protected_tag_edit.js @@ -21,26 +21,33 @@ export default class ProtectedTagEdit { } onSelect() { - const $allowedToCreateInput = this.$wrap.find(`input[name="${this.$allowedToCreateDropdownButton.data('fieldName')}"]`); + const $allowedToCreateInput = this.$wrap.find( + `input[name="${this.$allowedToCreateDropdownButton.data('fieldName')}"]`, + ); // Do not update if one dropdown has not selected any option if (!$allowedToCreateInput.length) return; this.$allowedToCreateDropdownButton.disable(); - axios.patch(this.$wrap.data('url'), { - protected_tag: { - create_access_levels_attributes: [{ - id: this.$allowedToCreateDropdownButton.data('accessLevelId'), - access_level: $allowedToCreateInput.val(), - }], - }, - }).then(() => { - this.$allowedToCreateDropdownButton.enable(); - }).catch(() => { - this.$allowedToCreateDropdownButton.enable(); - - flash('Failed to update tag!', 'alert', document.querySelector('.js-protected-tags-list')); - }); + axios + .patch(this.$wrap.data('url'), { + protected_tag: { + create_access_levels_attributes: [ + { + id: this.$allowedToCreateDropdownButton.data('accessLevelId'), + access_level: $allowedToCreateInput.val(), + }, + ], + }, + }) + .then(() => { + this.$allowedToCreateDropdownButton.enable(); + }) + .catch(() => { + this.$allowedToCreateDropdownButton.enable(); + + flash('Failed to update tag!', 'alert', document.querySelector('.js-protected-tags-list')); + }); } } diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue index 7e2287ac4db..9dd1c87a87d 100644 --- a/app/assets/javascripts/registry/components/app.vue +++ b/app/assets/javascripts/registry/components/app.vue @@ -1,42 +1,35 @@ <script> - import { mapGetters, mapActions } from 'vuex'; - import Flash from '../../flash'; - import store from '../stores'; - import collapsibleContainer from './collapsible_container.vue'; - import { errorMessages, errorMessagesTypes } from '../constants'; +import { mapGetters, mapActions } from 'vuex'; +import Flash from '../../flash'; +import store from '../stores'; +import collapsibleContainer from './collapsible_container.vue'; +import { errorMessages, errorMessagesTypes } from '../constants'; - export default { - name: 'RegistryListApp', - components: { - collapsibleContainer, +export default { + name: 'RegistryListApp', + components: { + collapsibleContainer, + }, + props: { + endpoint: { + type: String, + required: true, }, - props: { - endpoint: { - type: String, - required: true, - }, - }, - store, - computed: { - ...mapGetters([ - 'isLoading', - 'repos', - ]), - }, - created() { - this.setMainEndpoint(this.endpoint); - }, - mounted() { - this.fetchRepos() - .catch(() => Flash(errorMessages[errorMessagesTypes.FETCH_REPOS])); - }, - methods: { - ...mapActions([ - 'setMainEndpoint', - 'fetchRepos', - ]), - }, - }; + }, + store, + computed: { + ...mapGetters(['isLoading', 'repos']), + }, + created() { + this.setMainEndpoint(this.endpoint); + }, + mounted() { + this.fetchRepos().catch(() => Flash(errorMessages[errorMessagesTypes.FETCH_REPOS])); + }, + methods: { + ...mapActions(['setMainEndpoint', 'fetchRepos']), + }, +}; </script> <template> <div> diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue index d9bf41924d1..501b2625ae5 100644 --- a/app/assets/javascripts/registry/components/collapsible_container.vue +++ b/app/assets/javascripts/registry/components/collapsible_container.vue @@ -1,62 +1,59 @@ <script> - import { mapActions } from 'vuex'; - import Flash from '../../flash'; - import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; - import tooltip from '../../vue_shared/directives/tooltip'; - import tableRegistry from './table_registry.vue'; - import { errorMessages, errorMessagesTypes } from '../constants'; - import { __ } from '../../locale'; +import { mapActions } from 'vuex'; +import Flash from '../../flash'; +import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; +import tooltip from '../../vue_shared/directives/tooltip'; +import tableRegistry from './table_registry.vue'; +import { errorMessages, errorMessagesTypes } from '../constants'; +import { __ } from '../../locale'; - export default { - name: 'CollapsibeContainerRegisty', - components: { - clipboardButton, - tableRegistry, +export default { + name: 'CollapsibeContainerRegisty', + components: { + clipboardButton, + tableRegistry, + }, + directives: { + tooltip, + }, + props: { + repo: { + type: Object, + required: true, }, - directives: { - tooltip, - }, - props: { - repo: { - type: Object, - required: true, - }, - }, - data() { - return { - isOpen: false, - }; - }, - methods: { - ...mapActions([ - 'fetchRepos', - 'fetchList', - 'deleteRepo', - ]), + }, + data() { + return { + isOpen: false, + }; + }, + methods: { + ...mapActions(['fetchRepos', 'fetchList', 'deleteRepo']), - toggleRepo() { - this.isOpen = !this.isOpen; + toggleRepo() { + this.isOpen = !this.isOpen; - if (this.isOpen) { - this.fetchList({ repo: this.repo }) - .catch(() => this.showError(errorMessagesTypes.FETCH_REGISTRY)); - } - }, + if (this.isOpen) { + this.fetchList({ repo: this.repo }).catch(() => + this.showError(errorMessagesTypes.FETCH_REGISTRY), + ); + } + }, - handleDeleteRepository() { - this.deleteRepo(this.repo) - .then(() => { - Flash(__('This container registry has been scheduled for deletion.'), 'notice'); - this.fetchRepos(); - }) - .catch(() => this.showError(errorMessagesTypes.DELETE_REPO)); - }, + handleDeleteRepository() { + this.deleteRepo(this.repo) + .then(() => { + Flash(__('This container registry has been scheduled for deletion.'), 'notice'); + this.fetchRepos(); + }) + .catch(() => this.showError(errorMessagesTypes.DELETE_REPO)); + }, - showError(message) { - Flash(errorMessages[message]); - }, + showError(message) { + Flash(errorMessages[message]); }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue index fafb35bd69a..bb6c977fc63 100644 --- a/app/assets/javascripts/registry/components/table_registry.vue +++ b/app/assets/javascripts/registry/components/table_registry.vue @@ -1,66 +1,62 @@ <script> - import { mapActions } from 'vuex'; - import { n__ } from '../../locale'; - 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'; - import timeagoMixin from '../../vue_shared/mixins/timeago'; - import { errorMessages, errorMessagesTypes } from '../constants'; - import { numberToHumanSize } from '../../lib/utils/number_utils'; +import { mapActions } from 'vuex'; +import { n__ } from '../../locale'; +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'; +import timeagoMixin from '../../vue_shared/mixins/timeago'; +import { errorMessages, errorMessagesTypes } from '../constants'; +import { numberToHumanSize } from '../../lib/utils/number_utils'; - export default { - components: { - clipboardButton, - tablePagination, +export default { + components: { + clipboardButton, + tablePagination, + }, + directives: { + tooltip, + }, + mixins: [timeagoMixin], + props: { + repo: { + type: Object, + required: true, }, - directives: { - tooltip, + }, + computed: { + shouldRenderPagination() { + return this.repo.pagination.total > this.repo.pagination.perPage; }, - mixins: [ - timeagoMixin, - ], - props: { - repo: { - type: Object, - required: true, - }, - }, - computed: { - shouldRenderPagination() { - return this.repo.pagination.total > this.repo.pagination.perPage; - }, - }, - methods: { - ...mapActions([ - 'fetchList', - 'deleteRegistry', - ]), + }, + methods: { + ...mapActions(['fetchList', 'deleteRegistry']), - layers(item) { - return item.layers ? n__('%d layer', '%d layers', item.layers) : ''; - }, + layers(item) { + return item.layers ? n__('%d layer', '%d layers', item.layers) : ''; + }, - formatSize(size) { - return numberToHumanSize(size); - }, + formatSize(size) { + return numberToHumanSize(size); + }, - handleDeleteRegistry(registry) { - this.deleteRegistry(registry) - .then(() => this.fetchList({ repo: this.repo })) - .catch(() => this.showError(errorMessagesTypes.DELETE_REGISTRY)); - }, + handleDeleteRegistry(registry) { + this.deleteRegistry(registry) + .then(() => this.fetchList({ repo: this.repo })) + .catch(() => this.showError(errorMessagesTypes.DELETE_REGISTRY)); + }, - onPageChange(pageNumber) { - this.fetchList({ repo: this.repo, page: pageNumber }) - .catch(() => this.showError(errorMessagesTypes.FETCH_REGISTRY)); - }, + onPageChange(pageNumber) { + this.fetchList({ repo: this.repo, page: pageNumber }).catch(() => + this.showError(errorMessagesTypes.FETCH_REGISTRY), + ); + }, - showError(message) { - Flash(errorMessages[message]); - }, + showError(message) { + Flash(errorMessages[message]); }, - }; + }, +}; </script> <template> <div> diff --git a/app/assets/javascripts/registry/index.js b/app/assets/javascripts/registry/index.js index e15cd94a915..025afefe7f0 100644 --- a/app/assets/javascripts/registry/index.js +++ b/app/assets/javascripts/registry/index.js @@ -4,22 +4,23 @@ import Translate from '../vue_shared/translate'; Vue.use(Translate); -export default () => new Vue({ - el: '#js-vue-registry-images', - components: { - registryApp, - }, - data() { - const { dataset } = document.querySelector(this.$options.el); - return { - endpoint: dataset.endpoint, - }; - }, - render(createElement) { - return createElement('registry-app', { - props: { - endpoint: this.endpoint, - }, - }); - }, -}); +export default () => + new Vue({ + el: '#js-vue-registry-images', + components: { + registryApp, + }, + data() { + const { dataset } = document.querySelector(this.$options.el); + return { + endpoint: dataset.endpoint, + }; + }, + render(createElement) { + return createElement('registry-app', { + props: { + endpoint: this.endpoint, + }, + }); + }, + }); diff --git a/app/assets/javascripts/registry/stores/mutations.js b/app/assets/javascripts/registry/stores/mutations.js index 208c3c39866..69c051cd2d6 100644 --- a/app/assets/javascripts/registry/stores/mutations.js +++ b/app/assets/javascripts/registry/stores/mutations.js @@ -2,7 +2,6 @@ import * as types from './mutation_types'; import { parseIntPagination, normalizeHeaders } from '../../lib/utils/common_utils'; export default { - [types.SET_MAIN_ENDPOINT](state, endpoint) { Object.assign(state, { endpoint }); }, diff --git a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue index b373d83a44b..bd204503cc7 100644 --- a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue +++ b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue @@ -1,79 +1,72 @@ <script> - import { mapActions, mapGetters, mapState } from 'vuex'; - import { s__ } from '~/locale'; - import { componentNames } from './issue_body'; - import ReportSection from './report_section.vue'; - import SummaryRow from './summary_row.vue'; - import IssuesList from './issues_list.vue'; - import Modal from './modal.vue'; - import createStore from '../store'; - import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils'; +import { mapActions, mapGetters, mapState } from 'vuex'; +import { s__ } from '~/locale'; +import { componentNames } from './issue_body'; +import ReportSection from './report_section.vue'; +import SummaryRow from './summary_row.vue'; +import IssuesList from './issues_list.vue'; +import Modal from './modal.vue'; +import createStore from '../store'; +import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils'; - export default { - name: 'GroupedTestReportsApp', - store: createStore(), - components: { - ReportSection, - SummaryRow, - IssuesList, - Modal, +export default { + name: 'GroupedTestReportsApp', + store: createStore(), + components: { + ReportSection, + SummaryRow, + IssuesList, + Modal, + }, + props: { + endpoint: { + type: String, + required: true, }, - props: { - endpoint: { - type: String, - required: true, - }, - }, - componentNames, - computed: { - ...mapState([ - 'reports', - 'isLoading', - 'hasError', - 'summary', - ]), - ...mapState({ - modalTitle: state => state.modal.title || '', - modalData: state => state.modal.data || {}, - }), - ...mapGetters([ - 'summaryStatus', - ]), - groupedSummaryText() { - if (this.isLoading) { - return s__('Reports|Test summary results are being parsed'); - } + }, + componentNames, + computed: { + ...mapState(['reports', 'isLoading', 'hasError', 'summary']), + ...mapState({ + modalTitle: state => state.modal.title || '', + modalData: state => state.modal.data || {}, + }), + ...mapGetters(['summaryStatus']), + groupedSummaryText() { + if (this.isLoading) { + return s__('Reports|Test summary results are being parsed'); + } - if (this.hasError) { - return s__('Reports|Test summary failed loading results'); - } + if (this.hasError) { + return s__('Reports|Test summary failed loading results'); + } - return summaryTextBuilder(s__('Reports|Test summary'), this.summary); - }, + return summaryTextBuilder(s__('Reports|Test summary'), this.summary); }, - created() { - this.setEndpoint(this.endpoint); + }, + created() { + this.setEndpoint(this.endpoint); - this.fetchReports(); + this.fetchReports(); + }, + methods: { + ...mapActions(['setEndpoint', 'fetchReports']), + reportText(report) { + const summary = report.summary || {}; + return reportTextBuilder(report.name, summary); + }, + getReportIcon(report) { + return statusIcon(report.status); }, - methods: { - ...mapActions(['setEndpoint', 'fetchReports']), - reportText(report) { - const summary = report.summary || {}; - return reportTextBuilder(report.name, summary); - }, - getReportIcon(report) { - return statusIcon(report.status); - }, - shouldRenderIssuesList(report) { - return ( - report.existing_failures.length > 0 || - report.new_failures.length > 0 || - report.resolved_failures.length > 0 - ); - }, + shouldRenderIssuesList(report) { + return ( + report.existing_failures.length > 0 || + report.new_failures.length > 0 || + report.resolved_failures.length > 0 + ); }, - }; + }, +}; </script> <template> <report-section diff --git a/app/assets/javascripts/reports/components/issue_status_icon.vue b/app/assets/javascripts/reports/components/issue_status_icon.vue index 85811698a37..6e143c4f98c 100644 --- a/app/assets/javascripts/reports/components/issue_status_icon.vue +++ b/app/assets/javascripts/reports/components/issue_status_icon.vue @@ -1,10 +1,6 @@ <script> import Icon from '~/vue_shared/components/icon.vue'; -import { - STATUS_FAILED, - STATUS_NEUTRAL, - STATUS_SUCCESS, -} from '../constants'; +import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '../constants'; export default { name: 'IssueStatusIcon', diff --git a/app/assets/javascripts/reports/components/issues_list.vue b/app/assets/javascripts/reports/components/issues_list.vue index df42201b5de..3b425ee2fed 100644 --- a/app/assets/javascripts/reports/components/issues_list.vue +++ b/app/assets/javascripts/reports/components/issues_list.vue @@ -1,10 +1,6 @@ <script> import IssuesBlock from '~/reports/components/report_issues.vue'; -import { - STATUS_SUCCESS, - STATUS_FAILED, - STATUS_NEUTRAL, -} from '~/reports/constants'; +import { STATUS_SUCCESS, STATUS_FAILED, STATUS_NEUTRAL } from '~/reports/constants'; /** * Renders block of issues diff --git a/app/assets/javascripts/reports/components/modal.vue b/app/assets/javascripts/reports/components/modal.vue index acc5c6d85e2..5f9e4072b2d 100644 --- a/app/assets/javascripts/reports/components/modal.vue +++ b/app/assets/javascripts/reports/components/modal.vue @@ -1,27 +1,27 @@ <script> - import Modal from '~/vue_shared/components/gl_modal.vue'; - import LoadingButton from '~/vue_shared/components/loading_button.vue'; - import CodeBlock from '~/vue_shared/components/code_block.vue'; - import { fieldTypes } from '../constants'; +import Modal from '~/vue_shared/components/gl_modal.vue'; +import LoadingButton from '~/vue_shared/components/loading_button.vue'; +import CodeBlock from '~/vue_shared/components/code_block.vue'; +import { fieldTypes } from '../constants'; - export default { - components: { - Modal, - LoadingButton, - CodeBlock, +export default { + components: { + Modal, + LoadingButton, + CodeBlock, + }, + props: { + title: { + type: String, + required: true, }, - props: { - title: { - type: String, - required: true, - }, - modalData: { - type: Object, - required: true, - }, + modalData: { + type: Object, + required: true, }, - fieldTypes, - }; + }, + fieldTypes, +}; </script> <template> <modal diff --git a/app/assets/javascripts/reports/components/test_issue_body.vue b/app/assets/javascripts/reports/components/test_issue_body.vue index cd443a49b52..1a87822fcc3 100644 --- a/app/assets/javascripts/reports/components/test_issue_body.vue +++ b/app/assets/javascripts/reports/components/test_issue_body.vue @@ -1,28 +1,28 @@ <script> - import { mapActions } from 'vuex'; +import { mapActions } from 'vuex'; - export default { - name: 'TestIssueBody', - props: { - issue: { - type: Object, - required: true, - }, - // failed || success - status: { - type: String, - required: true, - }, - isNew: { - type: Boolean, - required: false, - default: false, - }, +export default { + name: 'TestIssueBody', + props: { + issue: { + type: Object, + required: true, }, - methods: { - ...mapActions(['openModal']), + // failed || success + status: { + type: String, + required: true, }, - }; + isNew: { + type: Boolean, + required: false, + default: false, + }, + }, + methods: { + ...mapActions(['openModal']), + }, +}; </script> <template> <div class="report-block-list-issue-description prepend-top-5 append-bottom-5"> diff --git a/app/assets/javascripts/reports/store/actions.js b/app/assets/javascripts/reports/store/actions.js index acabcc1d193..db8ab5ccb80 100644 --- a/app/assets/javascripts/reports/store/actions.js +++ b/app/assets/javascripts/reports/store/actions.js @@ -43,9 +43,11 @@ export const fetchReports = ({ state, dispatch }) => { }, data: state.endpoint, method: 'getReports', - successCallback: ({ data, status }) => dispatch('receiveReportsSuccess', { - data, status, - }), + successCallback: ({ data, status }) => + dispatch('receiveReportsSuccess', { + data, + status, + }), errorCallback: () => dispatch('receiveReportsError'), }); diff --git a/app/assets/javascripts/reports/store/index.js b/app/assets/javascripts/reports/store/index.js index 9d8f7dc3b74..467c692b438 100644 --- a/app/assets/javascripts/reports/store/index.js +++ b/app/assets/javascripts/reports/store/index.js @@ -7,9 +7,10 @@ import state from './state'; Vue.use(Vuex); -export default () => new Vuex.Store({ - actions, - mutations, - getters, - state: state(), -}); +export default () => + new Vuex.Store({ + actions, + mutations, + getters, + state: state(), + }); diff --git a/app/assets/javascripts/reports/store/mutation_types.js b/app/assets/javascripts/reports/store/mutation_types.js index 82bda31df5d..599d4862dfe 100644 --- a/app/assets/javascripts/reports/store/mutation_types.js +++ b/app/assets/javascripts/reports/store/mutation_types.js @@ -4,4 +4,3 @@ export const REQUEST_REPORTS = 'REQUEST_REPORTS'; export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS'; export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR'; export const SET_ISSUE_MODAL_DATA = 'SET_ISSUE_MODAL_DATA'; - diff --git a/app/assets/javascripts/reports/store/mutations.js b/app/assets/javascripts/reports/store/mutations.js index b88bff97075..2a37f5b74fa 100644 --- a/app/assets/javascripts/reports/store/mutations.js +++ b/app/assets/javascripts/reports/store/mutations.js @@ -19,7 +19,6 @@ export default { state.status = response.status; state.reports = response.suites; - }, [types.RECEIVE_REPORTS_ERROR](state) { state.isLoading = false; @@ -36,7 +35,7 @@ export default { [types.SET_ISSUE_MODAL_DATA](state, payload) { state.modal.title = payload.issue.name; - Object.keys(payload.issue).forEach((key) => { + Object.keys(payload.issue).forEach(key => { if (Object.prototype.hasOwnProperty.call(state.modal.data, key)) { state.modal.data[key] = { ...state.modal.data[key], diff --git a/app/assets/javascripts/reports/store/state.js b/app/assets/javascripts/reports/store/state.js index 4cab2e27a16..5484900276c 100644 --- a/app/assets/javascripts/reports/store/state.js +++ b/app/assets/javascripts/reports/store/state.js @@ -57,5 +57,4 @@ export default () => ({ }, }, }, - }); diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index 6b3753f7966..225e21ad322 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -21,7 +21,7 @@ Sidebar.initialize = function(currentUser) { } }; -Sidebar.prototype.removeListeners = function () { +Sidebar.prototype.removeListeners = function() { this.sidebar.off('click', '.sidebar-collapsed-icon'); this.sidebar.off('hidden.gl.dropdown'); $('.dropdown').off('loading.gl.dropdown'); @@ -38,10 +38,12 @@ Sidebar.prototype.addEventListeners = function() { $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); $document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked); - return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); + return $(document) + .off('click', '.js-issuable-todo') + .on('click', '.js-issuable-todo', this.toggleTodo); }; -Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { +Sidebar.prototype.sidebarToggleClicked = function(e, triggered) { var $allGutterToggleIcons, $this, isExpanded, tooltipLabel; e.preventDefault(); $this = $(this); @@ -51,18 +53,26 @@ Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { if (isExpanded) { $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); - $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - $('.layout-page').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + $('aside.right-sidebar') + .removeClass('right-sidebar-expanded') + .addClass('right-sidebar-collapsed'); + $('.layout-page') + .removeClass('right-sidebar-expanded') + .addClass('right-sidebar-collapsed'); } else { $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); - $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - $('.layout-page').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); + $('aside.right-sidebar') + .removeClass('right-sidebar-collapsed') + .addClass('right-sidebar-expanded'); + $('.layout-page') + .removeClass('right-sidebar-collapsed') + .addClass('right-sidebar-expanded'); } $this.attr('data-original-title', tooltipLabel); if (!triggered) { - Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); + Cookies.set('collapsed_gutter', $('.right-sidebar').hasClass('right-sidebar-collapsed')); } }; @@ -71,21 +81,27 @@ Sidebar.prototype.toggleTodo = function(e) { $this = $(e.currentTarget); ajaxType = $this.attr('data-delete-path') ? 'delete' : 'post'; if ($this.attr('data-delete-path')) { - url = "" + ($this.attr('data-delete-path')); + url = '' + $this.attr('data-delete-path'); } else { - url = "" + ($this.data('url')); + url = '' + $this.data('url'); } $this.tooltip('hide'); - $('.js-issuable-todo').disable().addClass('is-loading'); + $('.js-issuable-todo') + .disable() + .addClass('is-loading'); axios[ajaxType](url, { issuable_id: $this.data('issuableId'), issuable_type: $this.data('issuableType'), - }).then(({ data }) => { - this.todoUpdateDone(data); - }).catch(() => flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`)); + }) + .then(({ data }) => { + this.todoUpdateDone(data); + }) + .catch(() => + flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`), + ); }; Sidebar.prototype.todoUpdateDone = function(data) { @@ -99,7 +115,8 @@ Sidebar.prototype.todoUpdateDone = function(data) { const $el = $(el); const $elText = $el.find('.js-issuable-todo-inner'); - $el.removeClass('is-loading') + $el + .removeClass('is-loading') .enable() .attr('aria-label', $el.data(`${attrPrefix}Text`)) .attr('data-delete-path', deletePath) @@ -119,7 +136,9 @@ Sidebar.prototype.todoUpdateDone = function(data) { Sidebar.prototype.sidebarDropdownLoading = function(e) { var $loading, $sidebarCollapsedIcon, i, img; - $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); + $sidebarCollapsedIcon = $(this) + .closest('.block') + .find('.sidebar-collapsed-icon'); img = $sidebarCollapsedIcon.find('img'); i = $sidebarCollapsedIcon.find('i'); $loading = $('<i class="fa fa-spinner fa-spin"></i>'); @@ -134,7 +153,9 @@ Sidebar.prototype.sidebarDropdownLoading = function(e) { Sidebar.prototype.sidebarDropdownLoaded = function(e) { var $sidebarCollapsedIcon, i, img; - $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); + $sidebarCollapsedIcon = $(this) + .closest('.block') + .find('.sidebar-collapsed-icon'); img = $sidebarCollapsedIcon.find('img'); $sidebarCollapsedIcon.find('i.fa-spin').remove(); i = $sidebarCollapsedIcon.find('i'); @@ -220,7 +241,7 @@ Sidebar.prototype.isOpen = function() { }; Sidebar.prototype.getBlock = function(name) { - return this.sidebar.find(".block." + name); + return this.sidebar.find('.block.' + name); }; export default Sidebar; diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 7bde4860973..17def77b2d7 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -226,7 +226,7 @@ export class SearchAutocomplete { icon, text: term, template: s__('SearchAutocomplete|in all GitLab'), - url: `/search?search=${term}`, + url: `${gon.relative_url_root}/search?search=${term}`, }); if (template) { @@ -234,7 +234,9 @@ export class SearchAutocomplete { icon, text: term, template, - url: `/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`, + url: `${ + gon.relative_url_root + }/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`, }); } } diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue index 43f0b6651b9..8950ae31627 100644 --- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue +++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue @@ -5,6 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue'; import GfmAutoComplete from '~/gfm_auto_complete'; import { __, s__ } from '~/locale'; import Api from '~/api'; +import { GlModal } from '@gitlab-org/gitlab-ui'; import eventHub from './event_hub'; import EmojiMenuInModal from './emoji_menu_in_modal'; @@ -13,6 +14,7 @@ const emojiMenuClass = 'js-modal-status-emoji-menu'; export default { components: { Icon, + GlModal, }, props: { currentEmoji: { @@ -152,7 +154,7 @@ export default { </script> <template> - <gl-ui-modal + <gl-modal :title="s__('SetStatusModal|Set a status')" :modal-id="modalId" :ok-title="s__('SetStatusModal|Set status')" @@ -237,5 +239,5 @@ export default { </div> </div> </div> - </gl-ui-modal> + </gl-modal> </template> diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue index dd155c133ce..f1ea6aacdb2 100644 --- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue +++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue @@ -74,8 +74,8 @@ export default { } if (!this.users.length) { - const emptyTooltipLabel = this.issuableType === 'issue' ? - __('Assignee(s)') : __('Assignee'); + const emptyTooltipLabel = + this.issuableType === 'issue' ? __('Assignee(s)') : __('Assignee'); names.push(emptyTooltipLabel); } @@ -248,4 +248,3 @@ export default { </div> </div> </template> - diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue index 123c92aff64..cfa7029b388 100644 --- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue +++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue @@ -69,7 +69,8 @@ export default { this.loading = false; } - this.mediator.saveAssignees(this.field) + this.mediator + .saveAssignees(this.field) .then(setLoadingFalse.bind(this)) .catch(() => { setLoadingFalse(); 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 2b8d6207dea..439e8a69df0 100644 --- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue @@ -56,11 +56,7 @@ export default { .update('issue', { confidential }) .then(() => window.location.reload()) .catch(() => { - Flash( - __( - 'Something went wrong trying to change the confidentiality of this issue', - ), - ); + Flash(__('Something went wrong trying to change the confidentiality of this issue')); }); }, }, 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 cdff4105335..48a2b9194aa 100644 --- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue @@ -34,11 +34,7 @@ export default { required: true, type: Object, validator(mediatorObject) { - return ( - mediatorObject.service && - mediatorObject.service.update && - mediatorObject.store - ); + return mediatorObject.service && mediatorObject.service.update && mediatorObject.store; }, }, }, @@ -67,8 +63,7 @@ export default { methods: { toggleForm() { - this.mediator.store.isLockDialogOpen = !this.mediator.store - .isLockDialogOpen; + this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen; }, updateLockedAttribute(locked) { @@ -79,9 +74,14 @@ export default { .then(() => window.location.reload()) .catch(() => Flash( - sprintf(__('Something went wrong trying to change the locked state of this %{issuableDisplayName}'), { - issuableDisplayName: this.issuableDisplayName, - }), + sprintf( + __( + 'Something went wrong trying to change the locked state of this %{issuableDisplayName}', + ), + { + issuableDisplayName: this.issuableDisplayName, + }, + ), ), ); }, diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue index 286a16f7bbf..11b5dbe5f8e 100644 --- a/app/assets/javascripts/sidebar/components/participants/participants.vue +++ b/app/assets/javascripts/sidebar/components/participants/participants.vue @@ -1,78 +1,78 @@ <script> - import { __, n__, sprintf } from '~/locale'; - import tooltip from '~/vue_shared/directives/tooltip'; - import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; +import { __, n__, sprintf } from '~/locale'; +import tooltip from '~/vue_shared/directives/tooltip'; +import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; - export default { - directives: { - tooltip, +export default { + directives: { + tooltip, + }, + components: { + userAvatarImage, + }, + props: { + loading: { + type: Boolean, + required: false, + default: false, }, - components: { - 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, + }; + }, + computed: { + lessParticipants() { + return this.participants.slice(0, 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, - }); - } + 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; - }, - onClickCollapsedIcon() { - this.$emit('toggleSidebar'); - }, + onClickCollapsedIcon() { + this.$emit('toggleSidebar'); }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue b/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue index 5c1ead1a8ac..4ac515e552a 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 { - components: { - participants, +export default { + components: { + participants, + }, + props: { + mediator: { + type: Object, + required: true, }, - props: { - mediator: { - type: Object, - required: true, - }, - }, - data() { - return { - store: new Store(), - }; - }, - }; + }, + 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 385717e7c1e..95a2c8cce6e 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue @@ -21,10 +21,9 @@ export default { }, methods: { onToggleSubscription() { - this.mediator.toggleSubscription() - .catch(() => { - Flash(__('Error occurred when toggling the notification subscription')); - }); + this.mediator.toggleSubscription().catch(() => { + Flash(__('Error occurred when toggling the notification subscription')); + }); }, }, }; diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue index 448c8fc3602..b6151aa6c64 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue @@ -1,74 +1,74 @@ <script> - import { __ } from '~/locale'; - import icon from '~/vue_shared/components/icon.vue'; - import toggleButton from '~/vue_shared/components/toggle_button.vue'; - import tooltip from '~/vue_shared/directives/tooltip'; - import eventHub from '../../event_hub'; +import { __ } from '~/locale'; +import icon from '~/vue_shared/components/icon.vue'; +import toggleButton from '~/vue_shared/components/toggle_button.vue'; +import tooltip from '~/vue_shared/directives/tooltip'; +import eventHub from '../../event_hub'; - const ICON_ON = 'notifications'; - const ICON_OFF = 'notifications-off'; - const LABEL_ON = __('Notifications on'); - const LABEL_OFF = __('Notifications off'); +const ICON_ON = 'notifications'; +const ICON_OFF = 'notifications-off'; +const LABEL_ON = __('Notifications on'); +const LABEL_OFF = __('Notifications off'); - export default { - directives: { - tooltip, +export default { + directives: { + tooltip, + }, + components: { + icon, + toggleButton, + }, + props: { + loading: { + type: Boolean, + required: false, + default: false, }, - components: { - icon, - toggleButton, + subscribed: { + type: Boolean, + required: false, + default: null, }, - props: { - loading: { - type: Boolean, - required: false, - default: false, - }, - subscribed: { - type: Boolean, - required: false, - default: null, - }, - id: { - type: Number, - required: false, - default: null, - }, + id: { + type: Number, + required: false, + default: null, }, - computed: { - showLoadingState() { - return this.subscribed === null; - }, - notificationIcon() { - return this.subscribed ? ICON_ON : ICON_OFF; - }, - notificationTooltip() { - return this.subscribed ? LABEL_ON : LABEL_OFF; - }, + }, + computed: { + showLoadingState() { + return this.subscribed === null; }, - methods: { - /** - * We need to emit this event on both component & eventHub - * for 2 dependencies; - * - * 1. eventHub: This component is used in Issue Boards sidebar - * where component template is part of HAML - * and event listeners are tied to app's eventHub. - * 2. Component: This compone is also used in Epics in EE - * where listeners are tied to component event. - */ - toggleSubscription() { - // App's eventHub event emission. - eventHub.$emit('toggleSubscription', this.id); + notificationIcon() { + return this.subscribed ? ICON_ON : ICON_OFF; + }, + notificationTooltip() { + return this.subscribed ? LABEL_ON : LABEL_OFF; + }, + }, + methods: { + /** + * We need to emit this event on both component & eventHub + * for 2 dependencies; + * + * 1. eventHub: This component is used in Issue Boards sidebar + * where component template is part of HAML + * and event listeners are tied to app's eventHub. + * 2. Component: This compone is also used in Epics in EE + * where listeners are tied to component event. + */ + toggleSubscription() { + // App's eventHub event emission. + eventHub.$emit('toggleSubscription', this.id); - // Component event emission. - this.$emit('toggleSubscription', this.id); - }, - onClickCollapsedIcon() { - this.$emit('toggleSidebar'); - }, + // Component event emission. + this.$emit('toggleSubscription', this.id); + }, + onClickCollapsedIcon() { + this.$emit('toggleSidebar'); }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue index 1d030c4f67f..259858e4b46 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue @@ -1,111 +1,111 @@ <script> - import { __, sprintf } from '~/locale'; - import { abbreviateTime } from '~/lib/utils/pretty_time'; - import icon from '~/vue_shared/components/icon.vue'; - import tooltip from '~/vue_shared/directives/tooltip'; +import { __, sprintf } from '~/locale'; +import { abbreviateTime } from '~/lib/utils/datetime_utility'; +import icon from '~/vue_shared/components/icon.vue'; +import tooltip from '~/vue_shared/directives/tooltip'; - export default { - name: 'TimeTrackingCollapsedState', - components: { - icon, +export default { + name: 'TimeTrackingCollapsedState', + components: { + icon, + }, + directives: { + tooltip, + }, + props: { + showComparisonState: { + type: Boolean, + required: true, }, - directives: { - tooltip, + showSpentOnlyState: { + type: Boolean, + required: true, }, - props: { - showComparisonState: { - type: Boolean, - required: true, - }, - showSpentOnlyState: { - type: Boolean, - required: true, - }, - showEstimateOnlyState: { - type: Boolean, - required: true, - }, - showNoTimeTrackingState: { - type: Boolean, - required: true, - }, - timeSpentHumanReadable: { - type: String, - required: false, - default: '', - }, - timeEstimateHumanReadable: { - type: String, - required: false, - default: '', - }, + showEstimateOnlyState: { + type: Boolean, + required: true, }, - computed: { - timeSpent() { - return this.abbreviateTime(this.timeSpentHumanReadable); - }, - timeEstimate() { - return this.abbreviateTime(this.timeEstimateHumanReadable); - }, - divClass() { - if (this.showComparisonState) { - return 'compare'; - } else if (this.showEstimateOnlyState) { - return 'estimate-only'; - } else if (this.showSpentOnlyState) { - return 'spend-only'; - } else if (this.showNoTimeTrackingState) { - return 'no-tracking'; - } + showNoTimeTrackingState: { + type: Boolean, + required: true, + }, + timeSpentHumanReadable: { + type: String, + required: false, + default: '', + }, + timeEstimateHumanReadable: { + type: String, + required: false, + default: '', + }, + }, + computed: { + timeSpent() { + return this.abbreviateTime(this.timeSpentHumanReadable); + }, + timeEstimate() { + return this.abbreviateTime(this.timeEstimateHumanReadable); + }, + divClass() { + if (this.showComparisonState) { + return 'compare'; + } else if (this.showEstimateOnlyState) { + return 'estimate-only'; + } else if (this.showSpentOnlyState) { + return 'spend-only'; + } else if (this.showNoTimeTrackingState) { + return 'no-tracking'; + } + return ''; + }, + spanClass() { + if (this.showComparisonState) { return ''; - }, - spanClass() { - if (this.showComparisonState) { - return ''; - } else if (this.showEstimateOnlyState || this.showSpentOnlyState) { - return 'bold'; - } else if (this.showNoTimeTrackingState) { - return 'no-value'; - } + } else if (this.showEstimateOnlyState || this.showSpentOnlyState) { + return 'bold'; + } else if (this.showNoTimeTrackingState) { + return 'no-value'; + } - return ''; - }, - text() { - if (this.showComparisonState) { - return `${this.timeSpent} / ${this.timeEstimate}`; - } else if (this.showEstimateOnlyState) { - return `-- / ${this.timeEstimate}`; - } else if (this.showSpentOnlyState) { - return `${this.timeSpent} / --`; - } else if (this.showNoTimeTrackingState) { - return 'None'; - } + return ''; + }, + text() { + if (this.showComparisonState) { + return `${this.timeSpent} / ${this.timeEstimate}`; + } else if (this.showEstimateOnlyState) { + return `-- / ${this.timeEstimate}`; + } else if (this.showSpentOnlyState) { + return `${this.timeSpent} / --`; + } else if (this.showNoTimeTrackingState) { + return 'None'; + } - return ''; - }, - timeTrackedTooltipText() { - let title; - if (this.showComparisonState) { - title = __('Time remaining'); - } else if (this.showEstimateOnlyState) { - title = __('Estimated'); - } else if (this.showSpentOnlyState) { - title = __('Time spent'); - } + return ''; + }, + timeTrackedTooltipText() { + let title; + if (this.showComparisonState) { + title = __('Time remaining'); + } else if (this.showEstimateOnlyState) { + title = __('Estimated'); + } else if (this.showSpentOnlyState) { + title = __('Time spent'); + } - return sprintf('%{title}: %{text}', ({ title, text: this.text })); - }, - tooltipText() { - return this.showNoTimeTrackingState ? __('Time tracking') : this.timeTrackedTooltipText; - }, + return sprintf('%{title}: %{text}', { title, text: this.text }); + }, + tooltipText() { + return this.showNoTimeTrackingState ? __('Time tracking') : this.timeTrackedTooltipText; }, - methods: { - abbreviateTime(timeStr) { - return abbreviateTime(timeStr); - }, + }, + methods: { + abbreviateTime(timeStr) { + return abbreviateTime(timeStr); }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue index dc599e1b9fc..e74912d628f 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue @@ -1,5 +1,5 @@ <script> -import { parseSeconds, stringifyTime } from '../../../lib/utils/pretty_time'; +import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility'; import tooltip from '../../../vue_shared/directives/tooltip'; export default { diff --git a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue index 19ec0f05a26..91909cd49b8 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue @@ -15,16 +15,22 @@ export default { }, estimateText() { return sprintf( - s__('estimateCommand|%{slash_command} will update the estimated time with the latest command.'), { + s__( + 'estimateCommand|%{slash_command} will update the estimated time with the latest command.', + ), + { slash_command: '<code>/estimate</code>', - }, false, + }, + false, ); }, spendText() { return sprintf( - s__('spendCommand|%{slash_command} will update the sum of the time spent.'), { + s__('spendCommand|%{slash_command} will update the sum of the time spent.'), + { slash_command: '<code>/spend</code>', - }, false, + }, + false, ); }, }, diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue index 8660b0546cf..8e8b9f19b6e 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue @@ -26,7 +26,7 @@ export default { methods: { listenForQuickActions() { $(document).on('ajax:success', '.gfm-form', this.quickActionListened); - eventHub.$on('timeTrackingUpdated', (data) => { + eventHub.$on('timeTrackingUpdated', data => { this.quickActionListened(null, data); }); }, @@ -34,9 +34,7 @@ export default { const subscribedCommands = ['spend_time', 'time_estimate']; let changedCommands; if (data !== undefined) { - changedCommands = data.commands_changes - ? Object.keys(data.commands_changes) - : []; + changedCommands = data.commands_changes ? Object.keys(data.commands_changes) : []; } else { changedCommands = []; } diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue index a6b3a674952..bc59774f0a8 100644 --- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue +++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue @@ -41,9 +41,9 @@ export default { }, computed: { buttonClasses() { - return this.collapsed ? - 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state' : - 'btn btn-default btn-todo issuable-header-btn float-right'; + return this.collapsed + ? 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state' + : 'btn btn-default btn-todo issuable-header-btn float-right'; }, buttonLabel() { return this.isTodo ? MARK_TEXT : TODO_TEXT; diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js index b267422cd97..225ebb61195 100644 --- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js +++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js @@ -37,7 +37,8 @@ class SidebarMoveIssue { // Keep the dropdown open after selecting an option shouldPropagate: false, data: (searchTerm, callback) => { - this.mediator.fetchAutocompleteProjects(searchTerm) + this.mediator + .fetchAutocompleteProjects(searchTerm) .then(callback) .catch(() => new window.Flash('An error occurred while fetching projects autocomplete.')); }, @@ -48,7 +49,7 @@ class SidebarMoveIssue { </a> </li> `, - clicked: (options) => { + clicked: options => { const project = options.selectedObj; const selectedProjectId = options.isMarking ? project.id : 0; this.mediator.setMoveToProjectId(selectedProjectId); @@ -68,17 +69,12 @@ class SidebarMoveIssue { onConfirmClicked() { if (isValidProjectId(this.mediator.store.moveToProjectId)) { - this.$confirmButton - .disable() - .addClass('is-loading'); + this.$confirmButton.disable().addClass('is-loading'); - this.mediator.moveIssue() - .catch(() => { - window.Flash('An error occurred while moving the issue.'); - this.$confirmButton - .enable() - .removeClass('is-loading'); - }); + this.mediator.moveIssue().catch(() => { + window.Flash('An error occurred while moving the issue.'); + this.$confirmButton.enable().removeClass('is-loading'); + }); } } } diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js index 87da65a1b1f..1ebdbec7bc9 100644 --- a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js @@ -15,15 +15,16 @@ export default class SidebarMilestone { components: { timeTracker, }, - render: createElement => createElement('timeTracker', { - props: { - timeEstimate: parseInt(timeEstimate, 10), - timeSpent: parseInt(timeSpent, 10), - humanTimeEstimate, - humanTimeSpent, - rootPath: '/', - }, - }), + render: createElement => + createElement('timeTracker', { + props: { + timeEstimate: parseInt(timeEstimate, 10), + timeSpent: parseInt(timeSpent, 10), + humanTimeEstimate, + humanTimeSpent, + rootPath: '/', + }, + }), }); } } diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index 655bf9198b7..6f8214b18ee 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -22,14 +22,15 @@ function mountAssigneesComponent(mediator) { components: { SidebarAssignees, }, - render: createElement => createElement('sidebar-assignees', { - props: { - mediator, - field: el.dataset.field, - signedIn: el.hasAttribute('data-signed-in'), - issuableType: gl.utils.isInIssuePage() ? 'issue' : 'merge_request', - }, - }), + render: createElement => + createElement('sidebar-assignees', { + props: { + mediator, + field: el.dataset.field, + signedIn: el.hasAttribute('data-signed-in'), + issuableType: gl.utils.isInIssuePage() ? 'issue' : 'merge_request', + }, + }), }); } @@ -83,11 +84,12 @@ function mountParticipantsComponent(mediator) { components: { sidebarParticipants, }, - render: createElement => createElement('sidebar-participants', { - props: { - mediator, - }, - }), + render: createElement => + createElement('sidebar-participants', { + props: { + mediator, + }, + }), }); } @@ -102,11 +104,12 @@ function mountSubscriptionsComponent(mediator) { components: { sidebarSubscriptions, }, - render: createElement => createElement('sidebar-subscriptions', { - props: { - mediator, - }, - }), + render: createElement => + createElement('sidebar-subscriptions', { + props: { + mediator, + }, + }), }); } diff --git a/app/assets/javascripts/sidebar/services/sidebar_service.js b/app/assets/javascripts/sidebar/services/sidebar_service.js index 37c97225bfd..cbe20f761ff 100644 --- a/app/assets/javascripts/sidebar/services/sidebar_service.js +++ b/app/assets/javascripts/sidebar/services/sidebar_service.js @@ -22,11 +22,15 @@ export default class SidebarService { } update(key, data) { - return Vue.http.put(this.endpoint, { - [key]: data, - }, { - emulateJSON: true, - }); + return Vue.http.put( + this.endpoint, + { + [key]: data, + }, + { + emulateJSON: true, + }, + ); } getProjectsAutocomplete(searchTerm) { diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js index d9ca5e46770..3e040ec8428 100644 --- a/app/assets/javascripts/sidebar/sidebar_mediator.js +++ b/app/assets/javascripts/sidebar/sidebar_mediator.js @@ -39,9 +39,10 @@ export default class SidebarMediator { } fetch() { - return this.service.get() + return this.service + .get() .then(response => response.json()) - .then((data) => { + .then(data => { this.processFetchedData(data); }) .catch(() => new Flash('Error occurred when fetching sidebar data')); @@ -56,30 +57,33 @@ export default class SidebarMediator { toggleSubscription() { this.store.setFetchingState('subscriptions', true); - return this.service.toggleSubscription() + return this.service + .toggleSubscription() .then(() => { this.store.setSubscribedState(!this.store.subscribed); this.store.setFetchingState('subscriptions', false); }) - .catch((err) => { + .catch(err => { this.store.setFetchingState('subscriptions', false); throw err; }); } fetchAutocompleteProjects(searchTerm) { - return this.service.getProjectsAutocomplete(searchTerm) + return this.service + .getProjectsAutocomplete(searchTerm) .then(response => response.json()) - .then((data) => { + .then(data => { this.store.setAutocompleteProjects(data); return this.store.autocompleteProjects; }); } moveIssue() { - return this.service.moveIssue(this.store.moveToProjectId) + return this.service + .moveIssue(this.store.moveToProjectId) .then(response => response.json()) - .then((data) => { + .then(data => { if (window.location.pathname !== data.web_url) { visitUrl(data.web_url); } diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue index 6c87287a4c4..57c52a2016a 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -2,6 +2,7 @@ import Icon from '~/vue_shared/components/icon.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import FilteredSearchDropdown from '~/vue_shared/components/filtered_search_dropdown.vue'; +import { __ } from '~/locale'; import timeagoMixin from '../../vue_shared/mixins/timeago'; import tooltip from '../../vue_shared/directives/tooltip'; import LoadingButton from '../../vue_shared/components/loading_button.vue'; @@ -9,6 +10,7 @@ import { visitUrl } from '../../lib/utils/url_utility'; import createFlash from '../../flash'; import MemoryUsage from './memory_usage.vue'; import StatusIcon from './mr_widget_status_icon.vue'; +import ReviewAppLink from './review_app_link.vue'; import MRWidgetService from '../services/mr_widget_service'; export default { @@ -20,6 +22,7 @@ export default { Icon, TooltipOnTruncate, FilteredSearchDropdown, + ReviewAppLink, }, directives: { tooltip, @@ -31,6 +34,11 @@ export default { required: true, }, }, + deployedTextMap: { + running: __('Deploying to'), + success: __('Deployed to'), + failed: __('Failed to deploy to'), + }, data() { const features = window.gon.features || {}; return { @@ -54,10 +62,19 @@ export default { hasMetrics() { return !!this.deployment.metrics_url; }, + deployedText() { + return this.$options.deployedTextMap[this.deployment.status]; + }, + shouldRenderDropdown() { + return ( + this.enableCiEnvironmentsStatusChanges && + (this.deployment.changes && this.deployment.changes.length > 0) + ); + }, }, methods: { stopEnvironment() { - const msg = 'Are you sure you want to stop this environment?'; + const msg = __('Are you sure you want to stop this environment?'); const isConfirmed = confirm(msg); // eslint-disable-line if (isConfirmed) { @@ -87,10 +104,10 @@ export default { <div class="ci-widget media"> <div class="media-body"> <div class="deploy-body"> - <div class="deployment-info"> + <div class="js-deployment-info deployment-info"> <template v-if="hasDeploymentMeta"> <span> - Deployed to + {{ deployedText }} </span> <tooltip-on-truncate :title="deployment.name" @@ -124,7 +141,7 @@ export default { <div> <template v-if="hasExternalUrls"> <filtered-search-dropdown - v-if="enableCiEnvironmentsStatusChanges" + v-if="shouldRenderDropdown" class="js-mr-wigdet-deployment-dropdown inline" :items="deployment.changes" :main-action-link="deployment.external_url" @@ -134,18 +151,10 @@ export default { slot="mainAction" slot-scope="slotProps" > - <a - :href="deployment.external_url" - target="_blank" - rel="noopener noreferrer nofollow" - class="deploy-link js-deploy-url inline" - :class="slotProps.className" - > - <span> - {{ __('View app') }} - <icon name="external-link" /> - </span> - </a> + <review-app-link + :link="deployment.external_url" + :css-class="`deploy-link js-deploy-url inline ${slotProps.className}`" + /> </template> <template @@ -168,18 +177,11 @@ export default { </a> </template> </filtered-search-dropdown> - <a + <review-app-link v-else - :href="deployment.external_url" - target="_blank" - rel="noopener noreferrer nofollow" - class="js-deploy-url js-deploy-url-feature-flag deploy-link btn btn-default btn-sm inline" - > - <span> - {{ __('View app') }} - <icon name="external-link" /> - </span> - </a> + :link="deployment.external_url" + css-class="js-deploy-url js-deploy-url-feature-flag deploy-link btn btn-default btn-sm inlin" + /> </template> <loading-button v-if="deployment.stop_url" 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 fee41b239e8..8bcabc10225 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 @@ -1,5 +1,6 @@ <script> /* eslint-disable vue/require-default-prop */ +import { sprintf, __ } from '~/locale'; import PipelineStage from '~/pipelines/components/stage.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import Icon from '~/vue_shared/components/icon.vue'; @@ -36,6 +37,10 @@ export default { type: String, required: false, }, + troubleshootingDocsPath: { + type: String, + required: true, + }, }, computed: { hasPipeline() { @@ -57,6 +62,17 @@ export default { hasCommitInfo() { return this.pipeline.commit && Object.keys(this.pipeline.commit).length > 0; }, + errorText() { + return sprintf( + __( + 'Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}', + ), + { + linkStart: `<a href="${this.troubleshootingDocsPath}">`, + linkEnd: '</a>', + }, + ); + }, }, }; </script> @@ -77,8 +93,10 @@ export default { name="status_failed_borderless" /> </div> - <div class="media-body"> - Could not connect to the CI server. Please check your settings and try again + <div + class="media-body" + v-html="errorText" + > </div> </template> <template v-else-if="hasPipeline"> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue new file mode 100644 index 00000000000..b007d4f4dcb --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue @@ -0,0 +1,30 @@ +<script> +import Icon from '~/vue_shared/components/icon.vue'; + +export default { + components: { + Icon, + }, + props: { + link: { + type: String, + required: true, + }, + cssClass: { + type: String, + required: true, + }, + }, +}; +</script> +<template> + <a + :href="link" + target="_blank" + rel="noopener noreferrer nofollow" + :class="cssClass" + > + {{ __('View app') }} + <icon name="external-link" /> + </a> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue index c8ad2aa30a6..e7baecbcde4 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue @@ -71,7 +71,12 @@ export default { return defaultClass; }, iconClass() { - if (this.status === 'failed' || !this.commitMessage.length || !this.mr.isMergeAllowed || this.mr.preventMerge) { + if ( + this.status === 'failed' || + !this.commitMessage.length || + !this.mr.isMergeAllowed || + this.mr.preventMerge + ) { return 'warning'; } return 'success'; @@ -90,10 +95,12 @@ export default { }, isMergeButtonDisabled() { const { commitMessage } = this; - return Boolean(!commitMessage.length - || !this.shouldShowMergeControls() - || this.isMakingRequest - || this.mr.preventMerge); + return Boolean( + !commitMessage.length || + !this.shouldShowMergeControls() || + this.isMakingRequest || + this.mr.preventMerge, + ); }, isRemoveSourceBranchButtonDisabled() { return this.isMergeButtonDisabled; @@ -140,9 +147,10 @@ export default { }; this.isMakingRequest = true; - this.service.merge(options) + this.service + .merge(options) .then(res => res.data) - .then((data) => { + .then(data => { const hasError = data.status === 'failed' || data.status === 'hook_validation_error'; if (data.status === 'merge_when_pipeline_succeeds') { @@ -167,9 +175,10 @@ export default { }); }, handleMergePolling(continuePolling, stopPolling) { - this.service.poll() + this.service + .poll() .then(res => res.data) - .then((data) => { + .then(data => { if (data.state === 'merged') { // If state is merged we should update the widget and stop the polling eventHub.$emit('MRWidgetUpdateRequested'); @@ -205,9 +214,10 @@ export default { }); }, handleRemoveBranchPolling(continuePolling, stopPolling) { - this.service.poll() + this.service + .poll() .then(res => res.data) - .then((data) => { + .then(data => { // If source branch exists then we should continue polling // because removing a source branch is a background task and takes time if (data.source_branch_exists) { diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js deleted file mode 100644 index a23496c6bf5..00000000000 --- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * This file is the centerpiece of an attempt to reduce potential conflicts - * between the CE and EE versions of the MR widget. EE additions to the MR widget should - * be contained in the ee/vue_merge_request_widget directory, and should **extend** - * rather than mutate CE MR Widget code. - * - * This file should be the only source of conflicts between EE and CE. EE-only components should - * imported directly where they are needed, and import paths for EE extensions of CE components - * should overwrite import paths **without** changing the order of dependencies listed here. - */ - -export { default as Vue } from 'vue'; -export { default as SmartInterval } from '~/smart_interval'; -export { default as WidgetHeader } from './components/mr_widget_header.vue'; -export { default as WidgetMergeHelp } from './components/mr_widget_merge_help.vue'; -export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue'; -export { default as Deployment } from './components/deployment.vue'; -export { default as WidgetRelatedLinks } from './components/mr_widget_related_links.vue'; -export { default as MergedState } from './components/states/mr_widget_merged.vue'; -export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue'; -export { default as ClosedState } from './components/states/mr_widget_closed.vue'; -export { default as MergingState } from './components/states/mr_widget_merging.vue'; -export { default as WorkInProgressState } from './components/states/work_in_progress.vue'; -export { default as ArchivedState } from './components/states/mr_widget_archived.vue'; -export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue'; -export { default as NothingToMergeState } from './components/states/nothing_to_merge.vue'; -export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue'; -export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue'; -export { default as ReadyToMergeState } from './components/states/ready_to_merge.vue'; -export { default as ShaMismatchState } from './components/states/sha_mismatch.vue'; -export { default as UnresolvedDiscussionsState } from './components/states/unresolved_discussions.vue'; -export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue'; -export { default as PipelineFailedState } from './components/states/pipeline_failed.vue'; -export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue'; -export { default as RebaseState } from './components/states/mr_widget_rebase.vue'; -export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue'; -export { default as CheckingState } from './components/states/mr_widget_checking.vue'; -export { default as MRWidgetStore } from './stores/mr_widget_store'; -export { default as MRWidgetService } from './services/mr_widget_service'; -export { default as eventHub } from './event_hub'; -export { default as getStateKey } from './stores/get_state_key'; -export { default as stateMaps } from './stores/state_maps'; -export { default as SquashBeforeMerge } from './components/states/squash_before_merge.vue'; -export { default as notify } from '../lib/utils/notify'; -export { default as SourceBranchRemovalStatus } from './components/source_branch_removal_status.vue'; - -export { default as mrWidgetOptions } from './mr_widget_options.vue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/ee_switch_mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/ee_switch_mr_widget_options.js new file mode 100644 index 00000000000..8780aa4bd1c --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/ee_switch_mr_widget_options.js @@ -0,0 +1,3 @@ +import MRWidgetOptions from './mr_widget_options.vue'; + +export default MRWidgetOptions; diff --git a/app/assets/javascripts/vue_merge_request_widget/index.js b/app/assets/javascripts/vue_merge_request_widget/index.js index cc6e620f365..60cebbfc2b2 100644 --- a/app/assets/javascripts/vue_merge_request_widget/index.js +++ b/app/assets/javascripts/vue_merge_request_widget/index.js @@ -1,4 +1,5 @@ -import { Vue, mrWidgetOptions } from './dependencies'; +import Vue from 'vue'; +import MrWidgetOptions from './ee_switch_mr_widget_options'; import Translate from '../vue_shared/translate'; Vue.use(Translate); @@ -6,7 +7,7 @@ Vue.use(Translate); export default () => { gl.mrWidgetData.gitlabLogo = gon.gitlab_logo; - const vm = new Vue(mrWidgetOptions); + const vm = new Vue(MrWidgetOptions); window.gl.mrWidget = { checkStatus: vm.checkStatus, diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 8180f13a7cb..063d1e15544 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -1,40 +1,40 @@ <script> +import _ from 'underscore'; +import { __ } from '~/locale'; import Project from '~/pages/projects/project'; import SmartInterval from '~/smart_interval'; import createFlash from '../flash'; -import { - WidgetHeader, - WidgetMergeHelp, - WidgetPipeline, - Deployment, - WidgetRelatedLinks, - MergedState, - ClosedState, - MergingState, - RebaseState, - WorkInProgressState, - ArchivedState, - ConflictsState, - NothingToMergeState, - MissingBranchState, - NotAllowedState, - ReadyToMergeState, - ShaMismatchState, - UnresolvedDiscussionsState, - PipelineBlockedState, - PipelineFailedState, - FailedToMerge, - MergeWhenPipelineSucceedsState, - AutoMergeFailed, - CheckingState, - MRWidgetStore, - MRWidgetService, - eventHub, - stateMaps, - SquashBeforeMerge, - notify, - SourceBranchRemovalStatus, -} from './dependencies'; +import WidgetHeader from './components/mr_widget_header.vue'; +import WidgetMergeHelp from './components/mr_widget_merge_help.vue'; +import WidgetPipeline from './components/mr_widget_pipeline.vue'; +import Deployment from './components/deployment.vue'; +import WidgetRelatedLinks from './components/mr_widget_related_links.vue'; +import MergedState from './components/states/mr_widget_merged.vue'; +import ClosedState from './components/states/mr_widget_closed.vue'; +import MergingState from './components/states/mr_widget_merging.vue'; +import RebaseState from './components/states/mr_widget_rebase.vue'; +import WorkInProgressState from './components/states/work_in_progress.vue'; +import ArchivedState from './components/states/mr_widget_archived.vue'; +import ConflictsState from './components/states/mr_widget_conflicts.vue'; +import NothingToMergeState from './components/states/nothing_to_merge.vue'; +import MissingBranchState from './components/states/mr_widget_missing_branch.vue'; +import NotAllowedState from './components/states/mr_widget_not_allowed.vue'; +import ReadyToMergeState from './components/states/ready_to_merge.vue'; +import ShaMismatchState from './components/states/sha_mismatch.vue'; +import UnresolvedDiscussionsState from './components/states/unresolved_discussions.vue'; +import PipelineBlockedState from './components/states/mr_widget_pipeline_blocked.vue'; +import PipelineFailedState from './components/states/pipeline_failed.vue'; +import FailedToMerge from './components/states/mr_widget_failed_to_merge.vue'; +import MergeWhenPipelineSucceedsState from './components/states/mr_widget_merge_when_pipeline_succeeds.vue'; +import AutoMergeFailed from './components/states/mr_widget_auto_merge_failed.vue'; +import CheckingState from './components/states/mr_widget_checking.vue'; +import MRWidgetStore from './stores/ee_switch_mr_widget_store'; +import MRWidgetService from './services/ee_switch_mr_widget_service'; +import eventHub from './event_hub'; +import stateMaps from './stores/ee_switch_state_maps'; +import SquashBeforeMerge from './components/states/squash_before_merge.vue'; +import notify from '~/lib/utils/notify'; +import SourceBranchRemovalStatus from './components/source_branch_removal_status.vue'; import GroupedTestReportsApp from '../reports/components/grouped_test_reports_app.vue'; import { setFaviconOverlay } from '../lib/utils/common_utils'; @@ -82,6 +82,7 @@ export default { const service = this.createService(store); return { mr: store, + state: store.state, service, }; }, @@ -105,6 +106,17 @@ export default { (!this.mr.isNothingToMergeState && !this.mr.isMergedState) ); }, + shouldRenderMergedPipeline() { + return this.mr.state === 'merged' && !_.isEmpty(this.mr.mergePipeline); + }, + }, + watch: { + state(newVal, oldVal) { + if (newVal !== oldVal && this.shouldRenderMergedPipeline) { + // init polling + this.initPostMergeDeploymentsPolling(); + } + }, }, created() { this.initPolling(); @@ -114,11 +126,19 @@ export default { mounted() { this.setFaviconHelper(); this.initDeploymentsPolling(); + + if (this.shouldRenderMergedPipeline) { + this.initPostMergeDeploymentsPolling(); + } }, beforeDestroy() { eventHub.$off('mr.discussion.updated', this.checkStatus); this.pollingInterval.destroy(); this.deploymentsInterval.destroy(); + + if (this.postMergeDeploymentsInterval) { + this.postMergeDeploymentsInterval.destroy(); + } }, methods: { createService(store) { @@ -148,7 +168,13 @@ export default { cb.call(null, data); } }) - .catch(() => createFlash('Something went wrong. Please try again.')); + .catch(() => createFlash(__('Something went wrong. Please try again.'))); + }, + setFaviconHelper() { + if (this.mr.ciStatusFaviconPath) { + return setFaviconOverlay(this.mr.ciStatusFaviconPath); + } + return Promise.resolve(); }, initPolling() { this.pollingInterval = new SmartInterval({ @@ -160,8 +186,14 @@ export default { }); }, initDeploymentsPolling() { - this.deploymentsInterval = new SmartInterval({ - callback: this.fetchDeployments, + this.deploymentsInterval = this.deploymentsPoll(this.fetchPreMergeDeployments); + }, + initPostMergeDeploymentsPolling() { + this.postMergeDeploymentsInterval = this.deploymentsPoll(this.fetchPostMergeDeployments); + }, + deploymentsPoll(callback) { + return new SmartInterval({ + callback, startingInterval: 30000, maxInterval: 120000, hiddenInterval: 240000, @@ -169,26 +201,33 @@ export default { immediateExecution: true, }); }, - setFaviconHelper() { - if (this.mr.ciStatusFaviconPath) { - return setFaviconOverlay(this.mr.ciStatusFaviconPath); - } - return Promise.resolve(); + fetchDeployments(target) { + return this.service.fetchDeployments(target); }, - fetchDeployments() { - return this.service - .fetchDeployments() - .then(res => res.data) - .then(data => { + fetchPreMergeDeployments() { + return this.fetchDeployments() + .then(({ data }) => { if (data.length) { this.mr.deployments = data; } }) - .catch(() => { - createFlash( - 'Something went wrong while fetching the environments for this merge request. Please try again.', - ); - }); + .catch(() => this.throwDeploymentsError()); + }, + fetchPostMergeDeployments() { + return this.fetchDeployments('merge_commit') + .then(({ data }) => { + if (data.length) { + this.mr.postMergeDeployments = data; + } + }) + .catch(() => this.throwDeploymentsError()); + }, + throwDeploymentsError() { + createFlash( + __( + 'Something went wrong while fetching the environments for this merge request. Please try again.', + ), + ); }, fetchActionsContent() { this.service @@ -201,7 +240,7 @@ export default { Project.initRefSwitcher(); } }) - .catch(() => createFlash('Something went wrong. Please try again.')); + .catch(() => createFlash(__('Something went wrong. Please try again.'))); }, handleNotification(data) { if (data.ci_status === this.mr.ciStatus) return; @@ -266,10 +305,12 @@ export default { :has-ci="mr.hasCI" :source-branch="mr.sourceBranch" :source-branch-link="mr.sourceBranchLink" + :troubleshooting-docs-path="mr.troubleshootingDocsPath" /> <deployment v-for="deployment in mr.deployments" - :key="deployment.id" + :key="`pre-merge-deploy-${deployment.id}`" + class="js-pre-merge-deploy" :deployment="deployment" /> <div class="mr-section-container"> @@ -310,5 +351,23 @@ export default { <mr-widget-merge-help /> </div> </div> + + <template v-if="shouldRenderMergedPipeline"> + <mr-widget-pipeline + class="js-post-merge-pipeline prepend-top-default" + :pipeline="mr.mergePipeline" + :ci-status="mr.ciStatus" + :has-ci="mr.hasCI" + :source-branch="mr.targetBranch" + :source-branch-link="mr.targetBranch" + :troubleshooting-docs-path="mr.troubleshootingDocsPath" + /> + <deployment + v-for="postMergeDeployment in mr.postMergeDeployments" + :key="`post-merge-deploy-${postMergeDeployment.id}`" + :deployment="postMergeDeployment" + class="js-post-deployment" + /> + </template> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/services/ee_switch_mr_widget_service.js b/app/assets/javascripts/vue_merge_request_widget/services/ee_switch_mr_widget_service.js new file mode 100644 index 00000000000..ea2aabb78fe --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/services/ee_switch_mr_widget_service.js @@ -0,0 +1,3 @@ +import MRWidgetService from './mr_widget_service'; + +export default MRWidgetService; diff --git a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js index fecbfec2214..0bb70bfd658 100644 --- a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js +++ b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js @@ -21,8 +21,12 @@ export default class MRWidgetService { return axios.delete(this.endpoints.sourceBranchPath); } - fetchDeployments() { - return axios.get(this.endpoints.ciEnvironmentsStatusPath); + fetchDeployments(targetParam) { + return axios.get(this.endpoints.ciEnvironmentsStatusPath, { + params: { + environment_target: targetParam, + }, + }); } poll() { diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_get_state_key.js b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_get_state_key.js new file mode 100644 index 00000000000..ebef30e3eab --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_get_state_key.js @@ -0,0 +1,3 @@ +import getStateKey from './get_state_key'; + +export default getStateKey; diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_mr_widget_store.js new file mode 100644 index 00000000000..92a07c53f2d --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_mr_widget_store.js @@ -0,0 +1,3 @@ +import MergeRequestStore from './mr_widget_store'; + +export default MergeRequestStore; diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_state_maps.js b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_state_maps.js new file mode 100644 index 00000000000..50cf9503ea7 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_state_maps.js @@ -0,0 +1,3 @@ +import stateMaps from './state_maps'; + +export default stateMaps; diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 672e5280b5e..5c9a7133a6e 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -1,5 +1,5 @@ import Timeago from 'timeago.js'; -import { getStateKey } from '../dependencies'; +import getStateKey from './ee_switch_get_state_key'; import { stateKey } from './state_maps'; import { formatDate } from '../../lib/utils/datetime_utility'; @@ -18,6 +18,7 @@ export default class MergeRequestStore { this.squash = data.squash; this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath || data.squash_before_merge_help_path; + this.troubleshootingDocsPath = this.troubleshootingDocsPath || data.troubleshooting_docs_path; this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true; this.iid = data.iid; @@ -32,7 +33,9 @@ export default class MergeRequestStore { this.commitsCount = data.commits_count; this.divergedCommitsCount = data.diverged_commits_count; this.pipeline = data.pipeline || {}; + this.mergePipeline = data.merge_pipeline || {}; this.deployments = this.deployments || data.deployments || []; + this.postMergeDeployments = this.postMergeDeployments || []; this.initRebase(data); if (data.issues_links) { diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index 13bca99dcb3..151eee75d44 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -13,7 +13,7 @@ export default { }, props: { /** - * Indicates the existance of a tag. + * Indicates the existence of a tag. * Used to render the correct icon, if true will render `fa-tag` icon, * if false will render a svg sprite fork icon */ diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue index a07d63a495d..c78b96695cf 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue @@ -1,11 +1,11 @@ <script> -import { Link } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab-org/gitlab-ui'; import Icon from '../../icon.vue'; import { numberToHumanSize } from '../../../../lib/utils/number_utils'; export default { components: { - 'gl-link': Link, + GlLink, Icon, }, props: { diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue index 807e049caf6..419987d2c50 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue @@ -2,14 +2,14 @@ import axios from '~/lib/utils/axios_utils'; import { __ } from '~/locale'; import $ from 'jquery'; -import { SkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; const { CancelToken } = axios; let axiosSource; export default { components: { - SkeletonLoading, + GlSkeletonLoading, }, props: { content: { @@ -81,7 +81,7 @@ export default { <div ref="markdown-preview" class="md md-previewer"> - <skeleton-loading v-if="isLoading" /> + <gl-skeleton-loading v-if="isLoading" /> <div v-else v-html="previewContent"> diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue index 36a345130c0..2d89a156117 100644 --- a/app/assets/javascripts/vue_shared/components/file_row.vue +++ b/app/assets/javascripts/vue_shared/components/file_row.vue @@ -34,10 +34,21 @@ export default { required: false, default: false, }, + displayTextKey: { + type: String, + required: false, + default: 'name', + }, + shouldTruncateStart: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { mouseOver: false, + truncateStart: 0, }; }, computed: { @@ -60,6 +71,15 @@ export default { 'is-open': this.file.opened, }; }, + outputText() { + const text = this.file[this.displayTextKey]; + + if (this.truncateStart === 0) { + return text; + } + + return `...${text.substring(this.truncateStart, text.length)}`; + }, }, watch: { 'file.active': function fileActiveWatch(active) { @@ -72,6 +92,15 @@ export default { if (this.hasPathAtCurrentRoute()) { this.scrollIntoView(true); } + + if (this.shouldTruncateStart) { + const { scrollWidth, offsetWidth } = this.$refs.textOutput; + const textOverflow = scrollWidth - offsetWidth; + + if (textOverflow > 0) { + this.truncateStart = Math.ceil(textOverflow / 5) + 3; + } + } }, methods: { toggleTreeOpen(path) { @@ -139,6 +168,7 @@ export default { class="file-row-name-container" > <span + ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated" > @@ -156,7 +186,7 @@ export default { :size="16" class="append-right-5" /> - {{ file.name }} + {{ outputText }} </span> <component :is="extraComponent" @@ -175,6 +205,8 @@ export default { :hide-extra-on-tree="hideExtraOnTree" :extra-component="extraComponent" :show-changed-icon="showChangedIcon" + :display-text-key="displayTextKey" + :should-truncate-start="shouldTruncateStart" @toggleTreeOpen="toggleTreeOpen" @clickFile="clickedFile" /> diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue index 460fa6ad72e..388a2f4ca36 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue @@ -56,12 +56,14 @@ export default { filteredResults() { if (this.filter !== '') { return this.items.filter( - item => item[this.filterKey] && item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()), + item => + item[this.filterKey] && + item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()), ); } return this.items.slice(0, this.visibleItems); - } + }, }, mounted() { /** diff --git a/app/assets/javascripts/vue_shared/components/gl_countdown.vue b/app/assets/javascripts/vue_shared/components/gl_countdown.vue new file mode 100644 index 00000000000..9327a2a4a6c --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/gl_countdown.vue @@ -0,0 +1,49 @@ +<script> +import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility'; + +/** + * Counts down to a given end date. + */ +export default { + props: { + endDateString: { + type: String, + required: true, + validator(value) { + return !Number.isNaN(new Date(value).getTime()); + }, + }, + }, + + data() { + return { + remainingTime: formatTime(0), + countdownUpdateIntervalId: null, + }; + }, + + mounted() { + const updateRemainingTime = () => { + const remainingMilliseconds = calculateRemainingMilliseconds(this.endDateString); + this.remainingTime = formatTime(remainingMilliseconds); + }; + + updateRemainingTime(); + this.countdownUpdateIntervalId = window.setInterval(updateRemainingTime, 1000); + }, + + beforeDestroy() { + window.clearInterval(this.countdownUpdateIntervalId); + }, +}; +</script> + +<template> + <time + v-gl-tooltip + :datetime="endDateString" + :title="endDateString" + > + {{ remainingTime }} + </time> +</template> diff --git a/app/assets/javascripts/vue_shared/components/icon.vue b/app/assets/javascripts/vue_shared/components/icon.vue index 26f9d5ddc91..cddebfae115 100644 --- a/app/assets/javascripts/vue_shared/components/icon.vue +++ b/app/assets/javascripts/vue_shared/components/icon.vue @@ -8,7 +8,7 @@ let iconValidator = () => true; */ if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line global-require - const data = require('@gitlab-org/gitlab-svgs/dist/icons.json'); + const data = require('@gitlab/svgs/dist/icons.json'); const { icons } = data; iconValidator = value => { if (icons.includes(value)) { diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue index feb7b8f227e..b0a93794013 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue @@ -1,9 +1,9 @@ <script> -import { Link } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab-org/gitlab-ui'; export default { components: { - 'gl-link': Link, + GlLink, }, props: { markdownDocsPath: { diff --git a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue index 1d9c9220469..f56414c3c63 100644 --- a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue @@ -1,10 +1,10 @@ <script> -import { SkeletonLoading } from '@gitlab-org/gitlab-ui'; +import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui'; export default { name: 'SkeletonNote', components: { - SkeletonLoading, + GlSkeletonLoading, }, }; </script> @@ -17,7 +17,7 @@ export default { <div class="timeline-content"> <div class="note-header"></div> <div class="note-body"> - <skeleton-loading /> + <gl-skeleton-loading /> </div> </div> </div> diff --git a/app/assets/javascripts/vue_shared/components/pagination_links.vue b/app/assets/javascripts/vue_shared/components/pagination_links.vue index 1f2a679c145..89dcf049f6e 100644 --- a/app/assets/javascripts/vue_shared/components/pagination_links.vue +++ b/app/assets/javascripts/vue_shared/components/pagination_links.vue @@ -1,7 +1,11 @@ <script> +import { GlPagination } from '@gitlab-org/gitlab-ui'; import { s__ } from '../../locale'; export default { + components: { + GlPagination, + }, props: { change: { type: Function, diff --git a/app/assets/javascripts/vue_shared/components/pikaday.vue b/app/assets/javascripts/vue_shared/components/pikaday.vue index 782d8e3abf6..26c99aecae4 100644 --- a/app/assets/javascripts/vue_shared/components/pikaday.vue +++ b/app/assets/javascripts/vue_shared/components/pikaday.vue @@ -1,6 +1,6 @@ <script> import Pikaday from 'pikaday'; -import { parsePikadayDate, pikadayToString } from '../../lib/utils/datefix'; +import { parsePikadayDate, pikadayToString } from '~/lib/utils/datetime_utility'; export default { name: 'DatePicker', diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue index 7f1eb6bcec4..5841db52704 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue @@ -1,34 +1,50 @@ <script> - export default { - name: 'CollapsedCalendarIcon', - props: { - containerClass: { - type: String, - required: false, - default: '', - }, - text: { - type: String, - required: false, - default: '', - }, - showIcon: { - type: Boolean, - required: false, - default: true, - }, +import tooltip from '~/vue_shared/directives/tooltip'; + +export default { + name: 'CollapsedCalendarIcon', + directives: { + tooltip, + }, + props: { + containerClass: { + type: String, + required: false, + default: '', + }, + text: { + type: String, + required: false, + default: '', + }, + showIcon: { + type: Boolean, + required: false, + default: true, + }, + tooltipText: { + type: String, + required: false, + default: '', }, - methods: { - click() { - this.$emit('click'); - }, + }, + methods: { + click() { + this.$emit('click'); }, - }; + }, +}; </script> <template> <div + v-tooltip :class="containerClass" + :title="tooltipText" + data-container="body" + data-placement="left" + data-html="true" + data-boundary="viewport" @click="click" > <i diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue index dac438a702d..174c29809ac 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue @@ -1,88 +1,87 @@ <script> - import { dateInWords } from '../../../lib/utils/datetime_utility'; - import toggleSidebar from './toggle_sidebar.vue'; - import collapsedCalendarIcon from './collapsed_calendar_icon.vue'; +import { __ } from '~/locale'; +import timeagoMixin from '~/vue_shared/mixins/timeago'; +import { dateInWords, timeFor } from '~/lib/utils/datetime_utility'; +import collapsedCalendarIcon from './collapsed_calendar_icon.vue'; - export default { - name: 'SidebarCollapsedGroupedDatePicker', - components: { - toggleSidebar, - collapsedCalendarIcon, +export default { + name: 'SidebarCollapsedGroupedDatePicker', + components: { + collapsedCalendarIcon, + }, + mixins: [timeagoMixin], + props: { + collapsed: { + type: Boolean, + required: false, + default: true, }, - props: { - collapsed: { - type: Boolean, - required: false, - default: true, - }, - showToggleSidebar: { - type: Boolean, - required: false, - default: false, - }, - minDate: { - type: Date, - required: false, - default: null, - }, - maxDate: { - type: Date, - required: false, - default: null, - }, - disableClickableIcons: { - type: Boolean, - required: false, - default: false, - }, + minDate: { + type: Date, + required: false, + default: null, }, - computed: { - hasMinAndMaxDates() { - return this.minDate && this.maxDate; - }, - hasNoMinAndMaxDates() { - return !this.minDate && !this.maxDate; - }, - showMinDateBlock() { - return this.minDate || this.hasNoMinAndMaxDates; - }, - showFromText() { - return !this.maxDate && this.minDate; - }, - iconClass() { - const disabledClass = this.disableClickableIcons ? 'disabled' : ''; - return `block sidebar-collapsed-icon calendar-icon ${disabledClass}`; - }, + maxDate: { + type: Date, + required: false, + default: null, }, - methods: { - toggleSidebar() { - this.$emit('toggleCollapse'); - }, - dateText(dateType = 'min') { - const date = this[`${dateType}Date`]; - const dateWords = dateInWords(date, true); - const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords; + disableClickableIcons: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + hasMinAndMaxDates() { + return this.minDate && this.maxDate; + }, + hasNoMinAndMaxDates() { + return !this.minDate && !this.maxDate; + }, + showMinDateBlock() { + return this.minDate || this.hasNoMinAndMaxDates; + }, + showFromText() { + return !this.maxDate && this.minDate; + }, + iconClass() { + const disabledClass = this.disableClickableIcons ? 'disabled' : ''; + return `sidebar-collapsed-icon calendar-icon ${disabledClass}`; + }, + }, + methods: { + toggleSidebar() { + this.$emit('toggleCollapse'); + }, + dateText(dateType = 'min') { + const date = this[`${dateType}Date`]; + const dateWords = dateInWords(date, true); + const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords; + + return date ? parsedDateWords : __('None'); + }, + tooltipText(dateType = 'min') { + const defaultText = dateType === 'min' ? __('Start date') : __('Due date'); + const date = this[`${dateType}Date`]; + const timeAgo = dateType === 'min' ? this.timeFormated(date) : timeFor(date); + const dateText = date ? [this.dateText(dateType), `(${timeAgo})`].join(' ') : ''; - return date ? parsedDateWords : 'None'; - }, + if (date) { + return [defaultText, dateText].join('<br />'); + } + return __('Start and due date'); }, - }; + }, +}; </script> <template> <div class="block sidebar-grouped-item"> - <div - v-if="showToggleSidebar" - class="issuable-sidebar-header" - > - <toggle-sidebar - :collapsed="collapsed" - @toggle="toggleSidebar" - /> - </div> <collapsed-calendar-icon v-if="showMinDateBlock" :container-class="iconClass" + :tooltip-text="tooltipText('min')" @click="toggleSidebar" > <span class="sidebar-collapsed-value"> @@ -99,7 +98,7 @@ <collapsed-calendar-icon v-if="maxDate" :container-class="iconClass" - :show-icon="!minDate" + :tooltip-text="tooltipText('max')" @click="toggleSidebar" > <span class="sidebar-collapsed-value"> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue index af297f3c408..0d5fc07e6e3 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue @@ -14,7 +14,10 @@ export default { }, computed: { labelsList() { - const labelsString = this.labels.slice(0, 5).map(label => label.title).join(', '); + const labelsString = this.labels + .slice(0, 5) + .map(label => label.title) + .join(', '); if (this.labels.length > 5) { return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), { diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue index 14cb44b8619..86c7498a092 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue @@ -17,14 +17,14 @@ */ -import { Link } from '@gitlab-org/gitlab-ui'; +import { GlLink } from '@gitlab-org/gitlab-ui'; import userAvatarImage from './user_avatar_image.vue'; import tooltip from '../../directives/tooltip'; export default { name: 'UserAvatarLink', components: { - 'gl-link': Link, + GlLink, userAvatarImage, }, directives: { diff --git a/app/assets/javascripts/vue_shared/directives/tooltip.js b/app/assets/javascripts/vue_shared/directives/tooltip.js index 4f2412ce520..549d27e96d9 100644 --- a/app/assets/javascripts/vue_shared/directives/tooltip.js +++ b/app/assets/javascripts/vue_shared/directives/tooltip.js @@ -9,6 +9,14 @@ export default { componentUpdated(el) { $(el).tooltip('_fixTitle'); + + // update visible tooltips + const tooltipInstance = $(el).data('bs.tooltip'); + const tip = tooltipInstance.getTipElement(); + tooltipInstance.setElementContent( + $(tip.querySelectorAll('.tooltip-inner')), + tooltipInstance.getTitle(), + ); }, unbind(el) { |