diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2017-01-28 20:06:15 +0000 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2017-02-03 09:43:54 +0000 |
commit | 7ef21460d1ad47c1e140b5cf2977ebc90f8c6dd1 (patch) | |
tree | 1f757b363eea8f8db08c37d482bcb07deb78d255 | |
parent | 7ad626e348faaea6f186759dada36079d531f6fd (diff) | |
download | gitlab-ce-7ef21460d1ad47c1e140b5cf2977ebc90f8c6dd1.tar.gz |
Transform vue_pipelines index into a non-dependent table component.
7 files changed, 306 insertions, 312 deletions
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js.es6 b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js.es6 index d2547f0b4a8..d42f2d15f19 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js.es6 +++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js.es6 @@ -1,11 +1,10 @@ /* eslint-disable no-new */ -/* global Vue, VueResource */ +/* global Vue, CommitsPipelineStore, PipelinesService, Flash */ //= require vue +//= require_tree . +//= require vue //= require vue-resource -//= require ./pipelines_store -//= require ./pipelines_service -//= require vue_shared/components/commit //= require vue_shared/vue_resource_interceptor //= require vue_shared/components/pipelines_table @@ -21,18 +20,23 @@ * Necessary SVG in the table are provided as props. This should be refactored * as soon as we have Webpack and can load them directly into JS files. */ -(() => { +$(() => { window.gl = window.gl || {}; - gl.Commits = gl.Commits || {}; + gl.commits = gl.commits || {}; + gl.commits.pipelines = gl.commits.pipelines || {}; - if (gl.Commits.PipelinesTableView) { - gl.Commits.PipelinesTableView.$destroy(true); + if (gl.commits.PipelinesTableView) { + gl.commits.PipelinesTableView.$destroy(true); } - gl.Commits.PipelinesTableView = new Vue({ + gl.commits.pipelines.PipelinesTableView = new Vue({ el: document.querySelector('#commit-pipeline-table-view'), + components: { + 'pipelines-table-component': gl.pipelines.PipelinesTableComponent, + }, + /** * Accesses the DOM to provide the needed data. * Returns the necessary props to render `pipelines-table-component` component. @@ -41,21 +45,70 @@ */ data() { const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset; + const svgsData = document.querySelector('.pipeline-svgs').dataset; + const store = gl.commits.pipelines.PipelinesStore.create(); + + // Transform svgs DOMStringMap to a plain Object. + const svgsObject = Object.keys(svgsData).reduce((acc, element) => { + acc[element] = svgsData[element]; + return acc; + }, {}); return { - scope: pipelinesTableData.pipelinesData, - store: new CommitsPipelineStore(), - service: new PipelinesService(), - svgs: pipelinesTableData, + endpoint: pipelinesTableData.pipelinesData, + svgs: svgsObject, + store, + state: store.state, + isLoading: false, }; }, - components: { - 'pipelines-table-component': gl.pipelines.PipelinesTableComponent, + /** + * When the component is created the service to fetch the data will be + * initialized with the correct endpoint. + * + * A request to fetch the pipelines will be made. + * In case of a successfull response we will store the data in the provided + * store, in case of a failed response we need to warn the user. + * + */ + created() { + gl.pipelines.pipelinesService = new PipelinesService(this.endpoint); + + this.isLoading = true; + + return gl.pipelines.pipelinesService.all() + .then(response => response.json()) + .then((json) => { + this.store.store(json); + this.isLoading = false; + }).catch(() => { + this.isLoading = false; + new Flash('An error occurred while fetching the pipelines.', 'alert'); + }); }, template: ` - <pipelines-table-component :scope='scope' :store='store' :svgs='svgs'></pipelines-table-component> + <div> + <div class="pipelines realtime-loading" v-if='isLoading'> + <i class="fa fa-spinner fa-spin"></i> + </div> + + <div class="blank-state blank-state-no-icon" + v-if="!isLoading && state.pipelines.length === 0"> + <h2 class="blank-state-title js-blank-state-title"> + You don't have any pipelines. + </h2> + Put get started with pipelines button here!!! + </div> + + <div class="table-holder" v-if='!isLoading && state.pipelines.length > 0'> + <pipelines-table-component + :pipelines='state.pipelines' + :svgs='svgs'> + </pipelines-table-component> + </div> + </div> `, }); }); diff --git a/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 b/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 index 7d773e0d361..1e6aa73d9cf 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 +++ b/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 @@ -5,12 +5,8 @@ * Pipelines service. * * Used to fetch the data used to render the pipelines table. - * Used Vue.Resource + * Uses Vue.Resource */ - -window.gl = window.gl || {}; -gl.pipelines = gl.pipelines || {}; - class PipelinesService { constructor(root) { Vue.http.options.root = root; @@ -36,42 +32,3 @@ class PipelinesService { return this.pipelines.get(); } } - -gl.pipelines.PipelinesService = PipelinesService; - -// const pageValues = (headers) => { -// const normalized = gl.utils.normalizeHeaders(headers); -// -// const paginationInfo = { -// perPage: +normalized['X-PER-PAGE'], -// page: +normalized['X-PAGE'], -// total: +normalized['X-TOTAL'], -// totalPages: +normalized['X-TOTAL-PAGES'], -// nextPage: +normalized['X-NEXT-PAGE'], -// previousPage: +normalized['X-PREV-PAGE'], -// }; -// -// return paginationInfo; -// }; - -// gl.PipelineStore = class { -// fetchDataLoop(Vue, pageNum, url, apiScope) { -// const goFetch = () => -// this.$http.get(`${url}?scope=${apiScope}&page=${pageNum}`) -// .then((response) => { -// const pageInfo = pageValues(response.headers); -// this.pageInfo = Object.assign({}, this.pageInfo, pageInfo); -// -// const res = JSON.parse(response.body); -// this.count = Object.assign({}, this.count, res.count); -// this.pipelines = Object.assign([], this.pipelines, res); -// -// this.pageRequest = false; -// }, () => { -// this.pageRequest = false; -// return new Flash('Something went wrong on our end.'); -// }); -// -// goFetch(); -// } -// }; diff --git a/app/assets/javascripts/commit/pipelines/pipelines_store.js.es6 b/app/assets/javascripts/commit/pipelines/pipelines_store.js.es6 index bc748bece5d..5c2e1b33cd1 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_store.js.es6 +++ b/app/assets/javascripts/commit/pipelines/pipelines_store.js.es6 @@ -6,12 +6,14 @@ * Pipelines' Store for commits view. * * Used to store the Pipelines rendered in the commit view in the pipelines table. - * - * TODO: take care of timeago instances in here */ (() => { - const CommitPipelineStore = { + window.gl = window.gl || {}; + gl.commits = gl.commits || {}; + gl.commits.pipelines = gl.commits.pipelines || {}; + + gl.commits.pipelines.PipelinesStore = { state: {}, create() { @@ -20,11 +22,9 @@ return this; }, - storePipelines(pipelines = []) { + store(pipelines = []) { this.state.pipelines = pipelines; return pipelines; }, }; - - return CommitPipelineStore; })(); diff --git a/app/assets/javascripts/vue_pipelines_index/stage.js.es6 b/app/assets/javascripts/vue_pipelines_index/stage.js.es6 index 496df9aaced..572644c8e6e 100644 --- a/app/assets/javascripts/vue_pipelines_index/stage.js.es6 +++ b/app/assets/javascripts/vue_pipelines_index/stage.js.es6 @@ -14,6 +14,7 @@ type: Object, required: true, }, + //FIXME: DOMStringMap is non standard, let's use a plain object. svgs: { type: DOMStringMap, required: true, diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.js.es6 b/app/assets/javascripts/vue_shared/components/pipelines_table.js.es6 index f602a0c44c2..e606632306f 100644 --- a/app/assets/javascripts/vue_shared/components/pipelines_table.js.es6 +++ b/app/assets/javascripts/vue_shared/components/pipelines_table.js.es6 @@ -1,14 +1,14 @@ -/* eslint-disable no-param-reassign, no-new */ +/* eslint-disable no-param-reassign */ /* global Vue */ -/* global PipelinesService */ -/* global Flash */ -//= require vue_pipelines_index/status.js.es6 -//= require vue_pipelines_index/pipeline_url.js.es6 -//= require vue_pipelines_index/stage.js.es6 -//= require vue_pipelines_index/pipeline_actions.js.es6 -//= require vue_pipelines_index/time_ago.js.es6 -//= require vue_pipelines_index/pipelines.js.es6 +//=require ./pipelines_table_row + +/** + * Pipelines Table Component + * + * Given an array of pipelines, renders a table. + * + */ (() => { window.gl = window.gl || {}; @@ -17,31 +17,10 @@ gl.pipelines.PipelinesTableComponent = Vue.component('pipelines-table-component', { props: { - - /** - * Object used to store the Pipelines to render. - * It's passed as a prop to allow different stores to use this Component. - * Different API calls can result in different responses, using a custom - * store allows us to use the same pipeline component. - * - * Note: All provided stores need to have a `storePipelines` method. - * Find a better way to do this. - */ - store: { - type: Object, - required: true, - default: () => ({}), - }, - - /** - * Will be used to fetch the needed data. - * This component is used in different and therefore different API calls - * to different endpoints will be made. To guarantee this is a reusable - * component, the endpoint must be provided. - */ - endpoint: { - type: String, + pipelines: { + type: Array, required: true, + default: [], }, /** @@ -55,219 +34,31 @@ }, components: { - 'commit-component': gl.CommitComponent, - runningPipeline: gl.VueRunningPipeline, - pipelineActions: gl.VuePipelineActions, - 'vue-stage': gl.VueStage, - pipelineUrl: gl.VuePipelineUrl, - pipelineHead: gl.VuePipelineHead, - statusScope: gl.VueStatusScope, + 'pipelines-table-row-component': gl.pipelines.PipelinesTableRowComponent, }, - data() { - return { - state: this.store.state, - isLoading: false, - }; - }, - - computed: { - /** - * If provided, returns the commit tag. - * - * @returns {Object|Undefined} - */ - commitAuthor() { - if (this.pipeline && - this.pipeline.commit && - this.pipeline.commit.author) { - return this.pipeline.commit.author; - } - - return undefined; - }, - - /** - * If provided, returns the commit tag. - * - * @returns {String|Undefined} - */ - commitTag() { - if (this.model.last_deployment && - this.model.last_deployment.tag) { - return this.model.last_deployment.tag; - } - return undefined; - }, - - /** - * If provided, returns the commit ref. - * - * @returns {Object|Undefined} - */ - commitRef() { - if (this.pipeline.ref) { - return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => { - if (prop === 'url') { - accumulator.path = this.pipeline.ref[prop]; - } else { - accumulator[prop] = this.pipeline.ref[prop]; - } - return accumulator; - }, {}); - } - - return undefined; - }, - - /** - * If provided, returns the commit url. - * - * @returns {String|Undefined} - */ - commitUrl() { - if (this.pipeline.commit && - this.pipeline.commit.commit_path) { - return this.pipeline.commit.commit_path; - } - return undefined; - }, - - /** - * If provided, returns the commit short sha. - * - * @returns {String|Undefined} - */ - commitShortSha() { - if (this.pipeline.commit && - this.pipeline.commit.short_id) { - return this.pipeline.commit.short_id; - } - return undefined; - }, - - /** - * If provided, returns the commit title. - * - * @returns {String|Undefined} - */ - commitTitle() { - if (this.pipeline.commit && - this.pipeline.commit.title) { - return this.pipeline.commit.title; - } - return undefined; - }, - - /** - * Figure this out! - */ - author(pipeline) { - if (!pipeline.commit) return { avatar_url: '', web_url: '', username: '' }; - if (pipeline.commit.author) return pipeline.commit.author; - return { - avatar_url: pipeline.commit.author_gravatar_url, - web_url: `mailto:${pipeline.commit.author_email}`, - username: pipeline.commit.author_name, - }; - }, - - /** - * Figure this out - */ - match(string) { - return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase()); - }, - }, - - /** - * When the component is created the service to fetch the data will be - * initialized with the correct endpoint. - * - * A request to fetch the pipelines will be made. - * In case of a successfull response we will store the data in the provided - * store, in case of a failed response we need to warn the user. - * - */ - created() { - gl.pipelines.pipelinesService = new PipelinesService(this.endpoint); - - this.isLoading = true; - - return gl.pipelines.pipelinesService.all() - .then(resp => resp.json()) - .then((json) => { - this.store.storePipelines(json); - this.isLoading = false; - }).catch(() => { - this.isLoading = false; - new Flash('An error occurred while fetching the pipelines.', 'alert'); - }); - }, - // this need to be reusable between the 3 tables :/ template: ` - <div> - <div class="pipelines realtime-loading" v-if='isLoading'> - <i class="fa fa-spinner fa-spin"></i> - </div> - - - <div class="blank-state blank-state-no-icon" - v-if="!isLoading && state.pipelines.length === 0"> - <h2 class="blank-state-title js-blank-state-title"> - You don't have any pipelines. - </h2> - Put get started with pipelines button here!!! - </div> - - <div class="table-holder" v-if='!isLoading state.pipelines.length > 0'> - <table class="table ci-table"> - <thead> - <tr> - <th class="pipeline-status">Status</th> - <th class="pipeline-info">Pipeline</th> - <th class="pipeline-commit">Commit</th> - <th class="pipeline-stages">Stages</th> - <th class="pipeline-date"></th> - <th class="pipeline-actions hidden-xs"></th> - </tr> - </thead> - <tbody> - <tr class="commit" v-for='pipeline in state.pipelines'> - <status-scope - :pipeline='pipeline' - :match='match' - :svgs='svgs'> - </status-scope> - - <pipeline-url :pipeline='pipeline'></pipeline-url> - - <td> - <commit-component - :tag="commitTag" - :commit-ref="commitRef" - :commit-url="commitUrl" - :short-sha="commitShortSha" - :title="commitTitle" - :author="commitAuthor" - :commit-icon-svg="commitIconSvg"> - </commit-component> - </td> - - <td class="stage-cell"> - <div class="stage-container dropdown js-mini-pipeline-graph" v-for='stage in pipeline.details.stages'> - <vue-stage :stage='stage' :svgs='svgs' :match='match'></vue-stage> - </div> - </td> - - <time-ago :pipeline='pipeline' :svgs='svgs'></time-ago> - - <pipeline-actions :pipeline='pipeline' :svgs='svgs'></pipeline-actions> - </tr> - </tbody> - </table> - </div> - </div> + <table class="table ci-table"> + <thead> + <tr> + <th class="pipeline-status">Status</th> + <th class="pipeline-info">Pipeline</th> + <th class="pipeline-commit">Commit</th> + <th class="pipeline-stages">Stages</th> + <th class="pipeline-date"></th> + <th class="pipeline-actions hidden-xs"></th> + </tr> + </thead> + <tbody> + <template v-for="model in pipelines" + v-bind:model="model"> + <tr + is="pipelines-table-row-component" + :pipeline="model" + :svgs="svgs"></tr> + </template> + </tbody> + </table> `, }); })(); diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js.es6 b/app/assets/javascripts/vue_shared/components/pipelines_table_row.js.es6 new file mode 100644 index 00000000000..1e55cce1c41 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/pipelines_table_row.js.es6 @@ -0,0 +1,192 @@ +/* eslint-disable no-param-reassign */ +/* global Vue */ + +//= require vue_pipelines_index/status +//= require vue_pipelines_index/pipeline_url +//= require vue_pipelines_index/stage +//= require vue_shared/components/commit +//= require vue_pipelines_index/pipeline_actions +//= require vue_pipelines_index/time_ago +(() => { + window.gl = window.gl || {}; + gl.pipelines = gl.pipelines || {}; + + gl.pipelines.PipelinesTableRowComponent = Vue.component('pipelines-table-row-component', { + + props: { + pipeline: { + type: Object, + required: true, + default: () => ({}), + }, + + /** + * Remove this. Find a better way to do this. don't want to provide this 3 times. + */ + svgs: { + type: Object, + required: true, + default: () => ({}), + }, + }, + + components: { + 'commit-component': gl.CommitComponent, + runningPipeline: gl.VueRunningPipeline, + pipelineActions: gl.VuePipelineActions, + 'vue-stage': gl.VueStage, + pipelineUrl: gl.VuePipelineUrl, + pipelineHead: gl.VuePipelineHead, + statusScope: gl.VueStatusScope, + }, + + computed: { + /** + * If provided, returns the commit tag. + * Needed to render the commit component column. + * + * @returns {Object|Undefined} + */ + commitAuthor() { + if (this.pipeline && + this.pipeline.commit && + this.pipeline.commit.author) { + return this.pipeline.commit.author; + } + + return undefined; + }, + + /** + * If provided, returns the commit tag. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitTag() { + // if (this.model.last_deployment && + // this.model.last_deployment.tag) { + // return this.model.last_deployment.tag; + // } + return undefined; + }, + + /** + * If provided, returns the commit ref. + * Needed to render the commit component column. + * + * @returns {Object|Undefined} + */ + commitRef() { + if (this.pipeline.ref) { + return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => { + if (prop === 'url') { + accumulator.path = this.pipeline.ref[prop]; + } else { + accumulator[prop] = this.pipeline.ref[prop]; + } + return accumulator; + }, {}); + } + + return undefined; + }, + + /** + * If provided, returns the commit url. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitUrl() { + if (this.pipeline.commit && + this.pipeline.commit.commit_path) { + return this.pipeline.commit.commit_path; + } + return undefined; + }, + + /** + * If provided, returns the commit short sha. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitShortSha() { + if (this.pipeline.commit && + this.pipeline.commit.short_id) { + return this.pipeline.commit.short_id; + } + return undefined; + }, + + /** + * If provided, returns the commit title. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitTitle() { + if (this.pipeline.commit && + this.pipeline.commit.title) { + return this.pipeline.commit.title; + } + return undefined; + }, + + /** + * Figure this out! + * Needed to render the commit component column. + */ + author(pipeline) { + if (!pipeline.commit) return { avatar_url: '', web_url: '', username: '' }; + if (pipeline.commit.author) return pipeline.commit.author; + return { + avatar_url: pipeline.commit.author_gravatar_url, + web_url: `mailto:${pipeline.commit.author_email}`, + username: pipeline.commit.author_name, + }; + }, + }, + + methods: { + match(string) { + return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase()); + }, + }, + + template: ` + <tr class="commit"> + <status-scope + :pipeline='pipeline' + :svgs='svgs' + :match="match"> + </status-scope> + + <pipeline-url :pipeline='pipeline'></pipeline-url> + + <td> + <commit-component + :tag="commitTag" + :commit-ref="commitRef" + :commit-url="commitUrl" + :short-sha="commitShortSha" + :title="commitTitle" + :author="commitAuthor" + :commit-icon-svg="svgs.commitIconSvg"> + </commit-component> + </td> + + <td class="stage-cell"> + <div class="stage-container dropdown js-mini-pipeline-graph" v-for='stage in pipeline.details.stages'> + <vue-stage :stage='stage' :svgs='svgs' :match='match'></vue-stage> + </div> + </td> + + <time-ago :pipeline='pipeline' :svgs='svgs'></time-ago> + + <pipeline-actions :pipeline='pipeline' :svgs='svgs'></pipeline-actions> + </tr> + `, + }); +})(); diff --git a/app/views/projects/commit/pipelines.html.haml b/app/views/projects/commit/pipelines.html.haml index 09bd4288b9c..f62fbe4d9cd 100644 --- a/app/views/projects/commit/pipelines.html.haml +++ b/app/views/projects/commit/pipelines.html.haml @@ -3,9 +3,6 @@ = render 'commit_box' = render 'ci_menu' -- content_for :page_specific_javascripts do - = page_specific_javascript_tag("commit/pipelines/pipelines_bundle.js") - #commit-pipeline-table-view{ data: { pipelines_data: pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id)}} .pipeline-svgs{ data: { "commit_icon_svg" => custom_icon("icon_commit"), "icon_status_canceled" => custom_icon("icon_status_canceled"), @@ -28,3 +25,6 @@ "icon_timer" => custom_icon("icon_timer"), "icon_status_manual" => custom_icon("icon_status_manual"), } } + +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('commit/pipelines/pipelines_bundle.js') |