From b299198e1eb5e1f26d5267f4a64944e600086d6b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda <filipa@gitlab.com> Date: Wed, 3 Jan 2018 23:14:55 +0000 Subject: [PATCH] Adds `eslint-plugin-vue`, fixes linter errors and adds docs --- .eslintrc | 9 +- app/assets/javascripts/blob/notebook/index.js | 76 +++---- app/assets/javascripts/blob/pdf/index.js | 6 +- .../javascripts/boards/boards_bundle.js | 18 +- .../clusters/components/applications.vue | 32 +-- app/assets/javascripts/commit/image_file.js | 194 +++++++++--------- .../cycle_analytics/cycle_analytics_bundle.js | 20 +- app/assets/javascripts/deploy_keys/index.js | 6 +- .../components/environment_item.vue | 3 +- .../filtered_search/recent_searches_root.js | 6 +- .../groups/components/group_folder.vue | 6 +- .../groups/components/item_stats.vue | 7 +- .../ide/components/repo_commit_section.vue | 4 +- app/assets/javascripts/ide/index.js | 10 +- .../issue_show/components/description.vue | 6 +- .../javascripts/jobs/job_details_bundle.js | 12 +- .../merge_conflicts/merge_conflicts_bundle.js | 2 +- .../monitoring/components/empty_state.vue | 6 +- .../notes/components/comment_form.vue | 14 +- .../notes/components/noteable_discussion.vue | 3 +- .../graph/dropdown_job_component.vue | 3 +- .../pipelines/components/pipeline_url.vue | 12 +- .../pipelines/components/stage.vue | 4 +- .../pipelines/pipeline_details_bundle.js | 12 +- .../javascripts/pipelines/pipelines_bundle.js | 6 +- .../components/delete_account_modal.vue | 3 +- .../confidential_issue_sidebar.vue | 5 +- .../components/lock/lock_issue_sidebar.vue | 3 +- doc/development/fe_guide/style_guide_js.md | 14 +- package.json | 1 + spec/javascripts/boards/issue_card_spec.js | 6 +- .../components/markdown/field_spec.js | 6 +- yarn.lock | 44 ++++ 33 files changed, 333 insertions(+), 226 deletions(-) diff --git a/.eslintrc b/.eslintrc index 44ad6a4896c..a419dc521e8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,7 +4,10 @@ "browser": true, "es6": true }, - "extends": "airbnb-base", + "extends": [ + "airbnb-base", + "plugin:vue/recommended" + ], "globals": { "__webpack_public_path__": true, "_": false, @@ -12,7 +15,9 @@ "gon": false, "localStorage": false }, - "parser": "babel-eslint", + "parserOptions": { + "parser": "babel-eslint" + }, "plugins": [ "filenames", "import", diff --git a/app/assets/javascripts/blob/notebook/index.js b/app/assets/javascripts/blob/notebook/index.js index 57b031956e8..6f1350e80fc 100644 --- a/app/assets/javascripts/blob/notebook/index.js +++ b/app/assets/javascripts/blob/notebook/index.js @@ -8,6 +8,9 @@ export default () => { new Vue({ el, + components: { + notebookLab, + }, data() { return { error: false, @@ -16,8 +19,41 @@ export default () => { json: {}, }; }, - components: { - notebookLab, + mounted() { + if (gon.katex_css_url) { + const katexStyles = document.createElement('link'); + katexStyles.setAttribute('rel', 'stylesheet'); + katexStyles.setAttribute('href', gon.katex_css_url); + document.head.appendChild(katexStyles); + } + + if (gon.katex_js_url) { + const katexScript = document.createElement('script'); + katexScript.addEventListener('load', () => { + this.loadFile(); + }); + katexScript.setAttribute('src', gon.katex_js_url); + document.head.appendChild(katexScript); + } else { + this.loadFile(); + } + }, + methods: { + loadFile() { + axios.get(el.dataset.endpoint) + .then(res => res.data) + .then((data) => { + this.json = data; + this.loading = false; + }) + .catch((e) => { + if (e.status !== 200) { + this.loadError = true; + } + + this.error = true; + }); + }, }, template: ` <div class="container-fluid md prepend-top-default append-bottom-default"> @@ -46,41 +82,5 @@ export default () => { </p> </div> `, - methods: { - loadFile() { - axios.get(el.dataset.endpoint) - .then(res => res.data) - .then((data) => { - this.json = data; - this.loading = false; - }) - .catch((e) => { - if (e.status !== 200) { - this.loadError = true; - } - - this.error = true; - }); - }, - }, - mounted() { - if (gon.katex_css_url) { - const katexStyles = document.createElement('link'); - katexStyles.setAttribute('rel', 'stylesheet'); - katexStyles.setAttribute('href', gon.katex_css_url); - document.head.appendChild(katexStyles); - } - - if (gon.katex_js_url) { - const katexScript = document.createElement('script'); - katexScript.addEventListener('load', () => { - this.loadFile(); - }); - katexScript.setAttribute('src', gon.katex_js_url); - document.head.appendChild(katexScript); - } else { - this.loadFile(); - } - }, }); }; diff --git a/app/assets/javascripts/blob/pdf/index.js b/app/assets/javascripts/blob/pdf/index.js index 7109f356540..70136cc4087 100644 --- a/app/assets/javascripts/blob/pdf/index.js +++ b/app/assets/javascripts/blob/pdf/index.js @@ -7,6 +7,9 @@ export default () => { return new Vue({ el, + components: { + pdfLab, + }, data() { return { error: false, @@ -15,9 +18,6 @@ export default () => { pdf: el.dataset.endpoint, }; }, - components: { - pdfLab, - }, methods: { onLoad() { this.loading = false; diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js index 679c883cdcf..90166b3d3d1 100644 --- a/app/assets/javascripts/boards/boards_bundle.js +++ b/app/assets/javascripts/boards/boards_bundle.js @@ -171,19 +171,14 @@ $(() => { }); gl.IssueBoardsModalAddBtn = new Vue({ - mixins: [gl.issueBoards.ModalMixins], el: document.getElementById('js-add-issues-btn'), + mixins: [gl.issueBoards.ModalMixins], data() { return { modal: ModalStore.store, store: Store.state, }; }, - watch: { - disabled() { - this.updateTooltip(); - }, - }, computed: { disabled() { if (!this.store) { @@ -199,6 +194,14 @@ $(() => { return ''; }, }, + watch: { + disabled() { + this.updateTooltip(); + }, + }, + mounted() { + this.updateTooltip(); + }, methods: { updateTooltip() { const $tooltip = $(this.$refs.addIssuesButton); @@ -217,9 +220,6 @@ $(() => { } }, }, - mounted() { - this.updateTooltip(); - }, template: ` <div class="board-extra-actions"> <button diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index cd58b88db69..1b0bbffe37e 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -21,7 +21,9 @@ export default { computed: { generalApplicationDescription() { return sprintf( - _.escape(s__('ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}')), { + _.escape(s__(`ClusterIntegration|Install applications on your cluster. +Read more about %{helpLink}`)), + { helpLink: `<a href="${this.helpPath}"> ${_.escape(s__('ClusterIntegration|installing applications'))} </a>`, @@ -43,12 +45,15 @@ export default { )); const extraCostParagraph = sprintf( - _.escape(s__('ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}')), { - boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`, - pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb" target="_blank" rel="noopener noreferrer"> - ${_.escape(s__('ClusterIntegration|GKE pricing'))} - </a>`, - }, + _.escape(s__(`ClusterIntegration|%{boldNotice} This will add some +extra resources like a load balancer, +which incur additional costs. See %{pricingLink}`)), + { + boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`, + pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb" target="_blank" rel="noopener noreferrer"> + ${_.escape(s__('ClusterIntegration|GKE pricing'))} + </a>`, + }, false, ); @@ -69,11 +74,14 @@ export default { }, prometheusDescription() { return sprintf( - _.escape(s__('ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications.')), { - gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html", target="_blank" rel="noopener noreferrer"> - ${_.escape(s__('ClusterIntegration|Gitlab Integration'))} - </a>`, - }, + _.escape(s__(`ClusterIntegration|Prometheus is an open-source monitoring system +with %{gitlabIntegrationLink} to monitor deployed applications.`)), + { + gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html" +target="_blank" rel="noopener noreferrer"> + ${_.escape(s__('ClusterIntegration|Gitlab Integration'))} + </a>`, + }, false, ); }, diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js index b6a0ece7907..485d81882d2 100644 --- a/app/assets/javascripts/commit/image_file.js +++ b/app/assets/javascripts/commit/image_file.js @@ -23,6 +23,103 @@ export default class ImageFile { }); }; })(this)); + + this.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)); + }, + 'swipe': function() { + 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 = ref[0], maxHeight = ref[1]; + $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 = ref[0], maxHeight = ref[1]; + $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)); + } + }; } initViewModes() { @@ -95,103 +192,6 @@ export default class ImageFile { 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)); - }, - 'swipe': function() { - 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 = ref[0], maxHeight = ref[1]; - $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 = ref[0], maxHeight = ref[1]; - $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); if (domImg) { diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js index 49bb6c52180..034f2923b3b 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js @@ -20,6 +20,16 @@ $(() => { gl.cycleAnalyticsApp = new Vue({ el: '#cycle-analytics', name: 'CycleAnalytics', + components: { + banner, + 'stage-issue-component': stageComponent, + 'stage-plan-component': stagePlanComponent, + 'stage-code-component': stageCodeComponent, + 'stage-test-component': stageTestComponent, + 'stage-review-component': stageReviewComponent, + 'stage-staging-component': stageStagingComponent, + 'stage-production-component': stageComponent, + }, data() { const cycleAnalyticsEl = document.querySelector('#cycle-analytics'); const cycleAnalyticsService = new CycleAnalyticsService({ @@ -43,16 +53,6 @@ $(() => { return this.store.currentActiveStage(); }, }, - components: { - banner, - 'stage-issue-component': stageComponent, - 'stage-plan-component': stagePlanComponent, - 'stage-code-component': stageCodeComponent, - 'stage-test-component': stageTestComponent, - 'stage-review-component': stageReviewComponent, - 'stage-staging-component': stageStagingComponent, - 'stage-production-component': stageComponent, - }, created() { this.fetchCycleAnalyticsData(); }, diff --git a/app/assets/javascripts/deploy_keys/index.js b/app/assets/javascripts/deploy_keys/index.js index a5f232f950a..ca8798facc9 100644 --- a/app/assets/javascripts/deploy_keys/index.js +++ b/app/assets/javascripts/deploy_keys/index.js @@ -3,14 +3,14 @@ import deployKeysApp from './components/app.vue'; document.addEventListener('DOMContentLoaded', () => new Vue({ el: document.getElementById('js-deploy-keys'), + components: { + deployKeysApp, + }, data() { return { endpoint: this.$options.el.dataset.endpoint, }; }, - components: { - deployKeysApp, - }, render(createElement) { return createElement('deploy-keys-app', { props: { diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index 2f0e397aa45..f647eed6952 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -287,7 +287,8 @@ export default { if (this.model && this.model.last_deployment && this.model.last_deployment.deployable) { - return `${this.model.last_deployment.deployable.name} #${this.model.last_deployment.deployable.id}`; + const deployable = this.model.last_deployment.deployable; + return `${deployable.name} #${deployable.id}`; } return ''; }, diff --git a/app/assets/javascripts/filtered_search/recent_searches_root.js b/app/assets/javascripts/filtered_search/recent_searches_root.js index 27e49d4fb96..c99ed63c4af 100644 --- a/app/assets/javascripts/filtered_search/recent_searches_root.js +++ b/app/assets/javascripts/filtered_search/recent_searches_root.js @@ -32,6 +32,9 @@ class RecentSearchesRoot { const state = this.store.state; this.vm = new Vue({ el: this.wrapperElement, + components: { + 'recent-searches-dropdown-content': RecentSearchesDropdownContent, + }, data() { return state; }, template: ` <recent-searches-dropdown-content @@ -40,9 +43,6 @@ class RecentSearchesRoot { :allowed-keys="allowedKeys" /> `, - components: { - 'recent-searches-dropdown-content': RecentSearchesDropdownContent, - }, }); } diff --git a/app/assets/javascripts/groups/components/group_folder.vue b/app/assets/javascripts/groups/components/group_folder.vue index e60221fa08d..1ff984d02e2 100644 --- a/app/assets/javascripts/groups/components/group_folder.vue +++ b/app/assets/javascripts/groups/components/group_folder.vue @@ -20,7 +20,11 @@ export default { return this.parentGroup.childrenCount > MAX_CHILDREN_COUNT; }, moreChildrenStats() { - return n__('One more item', '%d more items', this.parentGroup.childrenCount - this.parentGroup.children.length); + return n__( + 'One more item', + '%d more items', + this.parentGroup.childrenCount - this.parentGroup.children.length + ); }, }, }; diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue index 9f8ac138fc3..d9bc0588908 100644 --- a/app/assets/javascripts/groups/components/item_stats.vue +++ b/app/assets/javascripts/groups/components/item_stats.vue @@ -1,6 +1,11 @@ <script> import tooltip from '../../vue_shared/directives/tooltip'; -import { ITEM_TYPE, VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE, PROJECT_VISIBILITY_TYPE } from '../constants'; +import { + ITEM_TYPE, + VISIBILITY_TYPE_ICON, + GROUP_VISIBILITY_TYPE, + PROJECT_VISIBILITY_TYPE, +} from '../constants'; export default { directives: { diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 470db2c9650..b1ec82f5209 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -49,7 +49,9 @@ export default { const createNewBranch = newBranch || this.startNewMR; const payload = { - branch: createNewBranch ? `${this.currentBranchId}-${new Date().getTime().toString()}` : this.currentBranchId, + branch: createNewBranch ? + `${this.currentBranchId}-${new Date().getTime().toString()}` : + this.currentBranchId, commit_message: this.commitMessage, actions: this.changedFiles.map(f => ({ action: f.tempFile ? 'create' : 'update', diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js index a96bd339f51..e7b0794596b 100644 --- a/app/assets/javascripts/ide/index.js +++ b/app/assets/javascripts/ide/index.js @@ -18,11 +18,6 @@ function initIde(el) { components: { ide, }, - methods: { - ...mapActions([ - 'setInitialData', - ]), - }, created() { const data = el.dataset; @@ -39,6 +34,11 @@ function initIde(el) { isInitialRoot: convertPermissionToBoolean(data.root), }); }, + methods: { + ...mapActions([ + 'setInitialData', + ]), + }, render(createElement) { return createElement('ide'); }, diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index c3f2bf130bb..317020f25eb 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -88,7 +88,11 @@ if (taskRegexMatches) { $tasks.text(this.taskStatus); - $tasksShort.text(`${taskRegexMatches[1]}/${taskRegexMatches[2]} task${taskRegexMatches[2] > 1 ? 's' : ''}`); + $tasksShort.text( + `${taskRegexMatches[1]}/${taskRegexMatches[2]} task${taskRegexMatches[2] > 1 ? + 's' : + ''}` + ); } else { $tasks.text(''); $tasksShort.text(''); diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js index baaf5641200..db53b04de0e 100644 --- a/app/assets/javascripts/jobs/job_details_bundle.js +++ b/app/assets/javascripts/jobs/job_details_bundle.js @@ -13,14 +13,14 @@ document.addEventListener('DOMContentLoaded', () => { // eslint-disable-next-line no-new new Vue({ el: '#js-build-header-vue', + components: { + jobHeader, + }, data() { return { mediator, }; }, - components: { - jobHeader, - }, mounted() { this.mediator.initBuildClass(); }, @@ -38,14 +38,14 @@ document.addEventListener('DOMContentLoaded', () => { // eslint-disable-next-line new Vue({ el: '#js-details-block-vue', + components: { + detailsBlock, + }, data() { return { mediator, }; }, - components: { - detailsBlock, - }, render(createElement) { return createElement('details-block', { props: { diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js index 94561d6b7c3..792b7523889 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js @@ -25,12 +25,12 @@ $(() => { gl.MergeConflictsResolverApp = new Vue({ el: '#conflicts', - data: mergeConflictsStore.state, components: { 'diff-file-editor': gl.mergeConflicts.diffFileEditor, 'inline-conflict-lines': gl.mergeConflicts.inlineConflictLines, 'parallel-conflict-lines': gl.mergeConflicts.parallelConflictLines }, + data: mergeConflictsStore.state, computed: { conflictsCountText() { return mergeConflictsStore.getConflictsCountText(); }, readyToCommit() { return mergeConflictsStore.isReadyToCommit(); }, diff --git a/app/assets/javascripts/monitoring/components/empty_state.vue b/app/assets/javascripts/monitoring/components/empty_state.vue index a18164482a2..9df7094b6ab 100644 --- a/app/assets/javascripts/monitoring/components/empty_state.vue +++ b/app/assets/javascripts/monitoring/components/empty_state.vue @@ -33,13 +33,15 @@ gettingStarted: { svgUrl: this.emptyGettingStartedSvgPath, title: 'Get started with performance monitoring', - description: 'Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments.', + description: `Stay updated about the performance and health +of your environment by configuring Prometheus to monitor your deployments.`, buttonText: 'Configure Prometheus', }, loading: { svgUrl: this.emptyLoadingSvgPath, title: 'Waiting for performance data', - description: 'Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available.', + description: `Creating graphs uses the data from the Prometheus server. +If this takes a long time, ensure that data is available.`, buttonText: 'View documentation', }, unableToConnect: { diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index e594377bc40..778db2f7132 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -65,7 +65,9 @@ if (this.note.length) { const actionText = this.isIssueOpen ? 'close' : 'reopen'; - return this.noteType === constants.COMMENT ? `Comment & ${actionText} issue` : `Start discussion & ${actionText} issue`; + return this.noteType === constants.COMMENT ? + `Comment & ${actionText} issue` : + `Start discussion & ${actionText} issue`; } return this.isIssueOpen ? 'Close issue' : 'Reopen issue'; @@ -159,7 +161,9 @@ .catch(() => { this.isSubmitting = false; this.discard(false); - const msg = 'Your comment could not be submitted! Please check your network connection and try again.'; + const msg = + `Your comment could not be submitted! +Please check your network connection and try again.`; Flash(msg, 'alert', this.$el); this.note = noteData.data.note.note; // Restore textarea content. this.removePlaceholderNotes(); @@ -207,7 +211,11 @@ }, initAutoSave() { if (this.isLoggedIn) { - this.autosave = new Autosave($(this.$refs.textarea), ['Note', 'Issue', this.getNoteableData.id], 'issue'); + this.autosave = new Autosave( + $(this.$refs.textarea), + ['Note', 'Issue', this.getNoteableData.id], + 'issue', + ); } }, initTaskList() { diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 11e8f805635..873c4f1ff96 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -130,7 +130,8 @@ this.removePlaceholderNotes(); this.isReplying = true; this.$nextTick(() => { - const msg = 'Your comment could not be submitted! Please check your network connection and try again.'; + const msg = `Your comment could not be submitted! +Please check your network connection and try again.`; Flash(msg, 'alert', this.$el); this.$refs.noteForm.note = noteText; callback(err); diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue index 7006d05e7b2..933d21bf17b 100644 --- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue @@ -59,7 +59,8 @@ * target the click event of this component. */ stopDropdownClickPropagation() { - $(this.$el.querySelectorAll('.js-grouped-pipeline-dropdown a.mini-pipeline-graph-dropdown-item')) + $(this.$el + .querySelectorAll('.js-grouped-pipeline-dropdown a.mini-pipeline-graph-dropdown-item')) .on('click', (e) => { e.stopPropagation(); }); diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue index 9da0aac50a1..1e2f45333a5 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue @@ -30,8 +30,16 @@ html: true, trigger: 'focus', placement: 'top', - title: '<div class="autodevops-title">This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b></div>', - content: `<a class="autodevops-link" href="${this.autoDevopsHelpPath}" target="_blank" rel="noopener noreferrer nofollow">Learn more about Auto DevOps</a>`, + title: `<div class="autodevops-title"> + This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b> + </div>`, + content: `<a + class="autodevops-link" + href="${this.autoDevopsHelpPath}" + target="_blank" + rel="noopener noreferrer nofollow"> + Learn more about Auto DevOps + </a>`, }; }, }, diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index ac9d9c901ca..021f271b267 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -116,7 +116,9 @@ export default { computed: { dropdownClass() { - return this.dropdownContent.length > 0 ? 'js-builds-dropdown-container' : 'js-builds-dropdown-loading'; + return this.dropdownContent.length > 0 ? + 'js-builds-dropdown-container' : + 'js-builds-dropdown-loading'; }, triggerButtonClass() { diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index 206023d4ddb..d88d280cb3f 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -15,14 +15,14 @@ document.addEventListener('DOMContentLoaded', () => { // eslint-disable-next-line new Vue({ el: '#js-pipeline-graph-vue', + components: { + pipelineGraph, + }, data() { return { mediator, }; }, - components: { - pipelineGraph, - }, render(createElement) { return createElement('pipeline-graph', { props: { @@ -36,14 +36,14 @@ document.addEventListener('DOMContentLoaded', () => { // eslint-disable-next-line new Vue({ el: '#js-pipeline-header-vue', + components: { + pipelineHeader, + }, data() { return { mediator, }; }, - components: { - pipelineHeader, - }, created() { eventHub.$on('headerPostAction', this.postAction); }, diff --git a/app/assets/javascripts/pipelines/pipelines_bundle.js b/app/assets/javascripts/pipelines/pipelines_bundle.js index 3e4b6eeb5bf..ab5596e70f0 100644 --- a/app/assets/javascripts/pipelines/pipelines_bundle.js +++ b/app/assets/javascripts/pipelines/pipelines_bundle.js @@ -7,6 +7,9 @@ Vue.use(Translate); document.addEventListener('DOMContentLoaded', () => new Vue({ el: '#pipelines-list-vue', + components: { + pipelinesComponent, + }, data() { const store = new PipelinesStore(); @@ -14,9 +17,6 @@ document.addEventListener('DOMContentLoaded', () => new Vue({ store, }; }, - components: { - pipelinesComponent, - }, render(createElement) { return createElement('pipelines-component', { props: { 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 78be6b6e884..67ed7cc0cdf 100644 --- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue +++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue @@ -51,7 +51,8 @@ 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. +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>`, 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 6ee4d487c0b..80927529ffe 100644 --- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue @@ -39,7 +39,10 @@ export default { updateConfidentialAttribute(confidential) { this.service.update('issue', { confidential }) .then(() => location.reload()) - .catch(() => new Flash('Something went wrong trying to change the confidentiality of this issue')); + .catch(() => { + 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 04c3a96bf74..bded18996eb 100644 --- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue @@ -54,7 +54,8 @@ export default { discussion_locked: locked, }) .then(() => location.reload()) - .catch(() => Flash(this.__(`Something went wrong trying to change the locked state of this ${this.issuableDisplayName}`))); + .catch(() => Flash(this.__(`Something went wrong trying to +change the locked state of this ${this.issuableDisplayName}`))); }, }, }; diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md index 1cd66f27492..3c5d69e1f71 100644 --- a/doc/development/fe_guide/style_guide_js.md +++ b/doc/development/fe_guide/style_guide_js.md @@ -101,16 +101,16 @@ followed by any global declarations, then a blank newline prior to any imports o ``` Import statements are following usual naming guidelines, for example object literals use camel case: - + ```javascript // some_object file export default { key: 'value', }; - + // bad import ObjectLiteral from 'some_object'; - + // good import objectLiteral from 'some_object'; ``` @@ -255,6 +255,10 @@ A forEach will cause side effects, it will be mutating the array being iterated. ### Vue.js +#### `eslint-vue-plugin` +We default to [eslint-vue-plugin][eslint-plugin-vue], with the `plugin:vue/recommended`. +Please check this [rules][eslint-plugin-vue-rules] for more documentation. + #### Basic Rules 1. The service has it's own file 1. The store has it's own file @@ -513,8 +517,8 @@ On those a default key should not be provided. 1. `props` 1. `mixins` 1. `directives` - 1. `data` 1. `components` + 1. `data` 1. `computedProps` 1. `methods` 1. `beforeCreate` @@ -582,3 +586,5 @@ The goal of this accord is to make sure we are all on the same page. [eslintrc]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.eslintrc [eslint-this]: http://eslint.org/docs/rules/class-methods-use-this [eslint-new]: http://eslint.org/docs/rules/no-new +[eslint-plugin-vue]: https://github.com/vuejs/eslint-plugin-vue +[eslint-plugin-vue-rules]: https://github.com/vuejs/eslint-plugin-vue#bulb-rules diff --git a/package.json b/package.json index 8c3932dccfd..f7fa71c2be6 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "dropzone": "^4.2.0", "emoji-unicode-version": "^0.2.1", "eslint-plugin-html": "^2.0.1", + "eslint-plugin-vue": "^4.0.1", "exports-loader": "^0.6.4", "file-loader": "^0.11.1", "fuzzaldrin-plus": "^0.5.0", diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js index 8ef221257be..278155c585e 100644 --- a/spec/javascripts/boards/issue_card_spec.js +++ b/spec/javascripts/boards/issue_card_spec.js @@ -45,6 +45,9 @@ describe('Issue card component', () => { component = new Vue({ el: document.querySelector('.test-container'), + components: { + 'issue-card': gl.issueBoards.IssueCardInner, + }, data() { return { list, @@ -53,9 +56,6 @@ describe('Issue card component', () => { rootPath: '/', }; }, - components: { - 'issue-card': gl.issueBoards.IssueCardInner, - }, template: ` <issue-card :issue="issue" diff --git a/spec/javascripts/vue_shared/components/markdown/field_spec.js b/spec/javascripts/vue_shared/components/markdown/field_spec.js index 24209be83fe..5f980bbf36c 100644 --- a/spec/javascripts/vue_shared/components/markdown/field_spec.js +++ b/spec/javascripts/vue_shared/components/markdown/field_spec.js @@ -12,14 +12,14 @@ describe('Markdown field component', () => { beforeEach((done) => { vm = new Vue({ + components: { + fieldComponent, + }, data() { return { text: 'testing\n123', }; }, - components: { - fieldComponent, - }, template: ` <field-component markdown-preview-path="/preview" diff --git a/yarn.lock b/yarn.lock index 381b1a243f8..7e6649db0c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -97,6 +97,10 @@ acorn@^5.0.0, acorn@^5.0.3, acorn@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75" +acorn@^5.2.1: + version "5.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822" + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -2397,6 +2401,24 @@ eslint-plugin-promise@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz#78fbb6ffe047201627569e85a6c5373af2a68fca" +eslint-plugin-vue@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-4.0.1.tgz#afda92cfd7e7363b1fbdb1a772dd63359a9ce96a" + dependencies: + require-all "^2.2.0" + vue-eslint-parser "^2.0.1" + +eslint-scope@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-visitor-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" + eslint@^3.10.1: version "3.19.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" @@ -2444,6 +2466,13 @@ espree@^3.4.0: acorn "^5.1.1" acorn-jsx "^3.0.0" +espree@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.2.tgz#756ada8b979e9dcfcdb30aad8d1a9304a905e1ca" + dependencies: + acorn "^5.2.1" + acorn-jsx "^3.0.0" + esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -5546,6 +5575,10 @@ request@^2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +require-all@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/require-all/-/require-all-2.2.0.tgz#b4420c233ac0282d0ff49b277fb880a8b5de0894" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -6504,6 +6537,17 @@ void-elements@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" +vue-eslint-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-2.0.1.tgz#30135771c4fad00fdbac4542a2d59f3b1d776834" + dependencies: + debug "^3.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.2" + esquery "^1.0.0" + lodash "^4.17.4" + vue-hot-reload-api@^2.2.0: version "2.2.4" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.2.4.tgz#683bd1d026c0d3b3c937d5875679e9a87ec6cd8f" -- 2.30.9