diff options
Diffstat (limited to 'app/assets/javascripts/pipelines/components')
16 files changed, 841 insertions, 832 deletions
diff --git a/app/assets/javascripts/pipelines/components/blank_state.vue b/app/assets/javascripts/pipelines/components/blank_state.vue index f3219b8291c..34360105176 100644 --- a/app/assets/javascripts/pipelines/components/blank_state.vue +++ b/app/assets/javascripts/pipelines/components/blank_state.vue @@ -1,18 +1,18 @@ <script> - export default { - name: 'PipelinesSvgState', - props: { - svgPath: { - type: String, - required: true, - }, +export default { + name: 'PipelinesSvgState', + props: { + svgPath: { + type: String, + required: true, + }, - message: { - type: String, - required: true, - }, + message: { + type: String, + required: true, }, - }; + }, +}; </script> <template> diff --git a/app/assets/javascripts/pipelines/components/empty_state.vue b/app/assets/javascripts/pipelines/components/empty_state.vue index 50c27bed9fd..c5a45afc634 100644 --- a/app/assets/javascripts/pipelines/components/empty_state.vue +++ b/app/assets/javascripts/pipelines/components/empty_state.vue @@ -1,21 +1,21 @@ <script> - export default { - name: 'PipelinesEmptyState', - props: { - helpPagePath: { - type: String, - required: true, - }, - emptyStateSvgPath: { - type: String, - required: true, - }, - canSetCi: { - type: Boolean, - required: true, - }, +export default { + name: 'PipelinesEmptyState', + props: { + helpPagePath: { + type: String, + required: true, }, - }; + emptyStateSvgPath: { + type: String, + required: true, + }, + canSetCi: { + type: Boolean, + required: true, + }, + }, +}; </script> <template> <div class="row empty-state js-empty-state"> diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue index 1f152ed438d..b82e28a0735 100644 --- a/app/assets/javascripts/pipelines/components/graph/action_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue @@ -41,7 +41,6 @@ export default { type: String, required: true, }, - }, data() { return { @@ -67,7 +66,8 @@ export default { this.isDisabled = true; - axios.post(`${this.link}.json`) + axios + .post(`${this.link}.json`) .then(() => { this.isDisabled = false; this.$emit('pipelineActionRequestComplete'); 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 e047d10ac93..c32dc83da8e 100644 --- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue @@ -109,6 +109,7 @@ export default { :key="i" > <job-component + :dropdown-length="job.size" :job="item" css-class-job-name="mini-pipeline-graph-dropdown-item" @pipelineActionRequestComplete="pipelineActionRequestComplete" diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue index 886e62ab1a7..8af984ef91a 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue @@ -46,6 +46,11 @@ export default { required: false, default: '', }, + dropdownLength: { + type: Number, + required: false, + default: Infinity, + }, }, computed: { status() { @@ -70,6 +75,10 @@ export default { return textBuilder.join(' '); }, + tooltipBoundary() { + return this.dropdownLength < 5 ? 'viewport' : null; + }, + /** * Verifies if the provided job has an action path * @@ -94,9 +103,9 @@ export default { :href="status.details_path" :title="tooltipText" :class="cssClassJobName" + :data-boundary="tooltipBoundary" data-container="body" data-html="true" - data-boundary="viewport" class="js-pipeline-graph-job-link" > diff --git a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue index 14f4964a406..6fdbcc1e049 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue @@ -1,28 +1,28 @@ <script> - import ciIcon from '../../../vue_shared/components/ci_icon.vue'; +import ciIcon from '../../../vue_shared/components/ci_icon.vue'; - /** - * Component that renders both the CI icon status and the job name. - * Used in - * - Badge component - * - Dropdown badge components - */ - export default { - components: { - ciIcon, +/** + * Component that renders both the CI icon status and the job name. + * Used in + * - Badge component + * - Dropdown badge components + */ +export default { + components: { + ciIcon, + }, + props: { + name: { + type: String, + required: true, }, - props: { - name: { - type: String, - required: true, - }, - status: { - type: Object, - required: true, - }, + status: { + type: Object, + required: true, }, - }; + }, +}; </script> <template> <span class="ci-job-name-component"> diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue index 5b212ee8931..001eaeaa065 100644 --- a/app/assets/javascripts/pipelines/components/header_component.vue +++ b/app/assets/javascripts/pipelines/components/header_component.vue @@ -1,81 +1,81 @@ <script> - import ciHeader from '../../vue_shared/components/header_ci_component.vue'; - import eventHub from '../event_hub'; - import loadingIcon from '../../vue_shared/components/loading_icon.vue'; +import ciHeader from '../../vue_shared/components/header_ci_component.vue'; +import eventHub from '../event_hub'; +import loadingIcon from '../../vue_shared/components/loading_icon.vue'; - export default { - name: 'PipelineHeaderSection', - components: { - ciHeader, - loadingIcon, +export default { + name: 'PipelineHeaderSection', + components: { + ciHeader, + loadingIcon, + }, + props: { + pipeline: { + type: Object, + required: true, }, - props: { - pipeline: { - type: Object, - required: true, - }, - isLoading: { - type: Boolean, - required: true, - }, - }, - data() { - return { - actions: this.getActions(), - }; + isLoading: { + type: Boolean, + required: true, }, + }, + data() { + return { + actions: this.getActions(), + }; + }, - computed: { - status() { - return this.pipeline.details && this.pipeline.details.status; - }, - shouldRenderContent() { - return !this.isLoading && Object.keys(this.pipeline).length; - }, + computed: { + status() { + return this.pipeline.details && this.pipeline.details.status; + }, + shouldRenderContent() { + return !this.isLoading && Object.keys(this.pipeline).length; }, + }, - watch: { - pipeline() { - this.actions = this.getActions(); - }, + watch: { + pipeline() { + this.actions = this.getActions(); }, + }, - methods: { - postAction(action) { - const index = this.actions.indexOf(action); + methods: { + postAction(action) { + const index = this.actions.indexOf(action); - this.$set(this.actions[index], 'isLoading', true); + this.$set(this.actions[index], 'isLoading', true); - eventHub.$emit('headerPostAction', action); - }, + eventHub.$emit('headerPostAction', action); + }, - getActions() { - const actions = []; + getActions() { + const actions = []; - if (this.pipeline.retry_path) { - actions.push({ - label: 'Retry', - path: this.pipeline.retry_path, - cssClass: 'js-retry-button btn btn-inverted-secondary', - type: 'button', - isLoading: false, - }); - } + if (this.pipeline.retry_path) { + actions.push({ + label: 'Retry', + path: this.pipeline.retry_path, + cssClass: 'js-retry-button btn btn-inverted-secondary', + type: 'button', + isLoading: false, + }); + } - if (this.pipeline.cancel_path) { - actions.push({ - label: 'Cancel running', - path: this.pipeline.cancel_path, - cssClass: 'js-btn-cancel-pipeline btn btn-danger', - type: 'button', - isLoading: false, - }); - } + if (this.pipeline.cancel_path) { + actions.push({ + label: 'Cancel running', + path: this.pipeline.cancel_path, + cssClass: 'js-btn-cancel-pipeline btn btn-danger', + type: 'button', + isLoading: false, + }); + } - return actions; - }, + return actions; }, - }; + }, +}; </script> <template> <div class="pipeline-header-container"> diff --git a/app/assets/javascripts/pipelines/components/nav_controls.vue b/app/assets/javascripts/pipelines/components/nav_controls.vue index 1fce9f16ee0..9501afb7493 100644 --- a/app/assets/javascripts/pipelines/components/nav_controls.vue +++ b/app/assets/javascripts/pipelines/components/nav_controls.vue @@ -1,42 +1,42 @@ <script> - import LoadingButton from '../../vue_shared/components/loading_button.vue'; +import LoadingButton from '../../vue_shared/components/loading_button.vue'; - export default { - name: 'PipelineNavControls', - components: { - LoadingButton, +export default { + name: 'PipelineNavControls', + components: { + LoadingButton, + }, + props: { + newPipelinePath: { + type: String, + required: false, + default: null, }, - props: { - newPipelinePath: { - type: String, - required: false, - default: null, - }, - resetCachePath: { - type: String, - required: false, - default: null, - }, + resetCachePath: { + type: String, + required: false, + default: null, + }, - ciLintPath: { - type: String, - required: false, - default: null, - }, + ciLintPath: { + type: String, + required: false, + default: null, + }, - isResetCacheButtonLoading: { - type: Boolean, - required: false, - default: false, - }, + isResetCacheButtonLoading: { + type: Boolean, + required: false, + default: false, }, - methods: { - onClickResetCache() { - this.$emit('resetRunnersCache', this.resetCachePath); - }, + }, + methods: { + onClickResetCache() { + this.$emit('resetRunnersCache', this.resetCachePath); }, - }; + }, +}; </script> <template> <div class="nav-controls"> diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue index a107e579457..75db1e9ae7c 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue @@ -1,49 +1,49 @@ <script> - import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; - import tooltip from '../../vue_shared/directives/tooltip'; - import popover from '../../vue_shared/directives/popover'; +import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; +import tooltip from '../../vue_shared/directives/tooltip'; +import popover from '../../vue_shared/directives/popover'; - export default { - components: { - userAvatarLink, +export default { + components: { + userAvatarLink, + }, + directives: { + tooltip, + popover, + }, + props: { + pipeline: { + type: Object, + required: true, }, - directives: { - tooltip, - popover, + autoDevopsHelpPath: { + type: String, + required: true, }, - props: { - pipeline: { - type: Object, - required: true, - }, - autoDevopsHelpPath: { - type: String, - required: true, - }, + }, + computed: { + user() { + return this.pipeline.user; }, - computed: { - user() { - return this.pipeline.user; - }, - popoverOptions() { - return { - html: true, - trigger: 'focus', - placement: 'top', - title: `<div class="autodevops-title"> + popoverOptions() { + return { + 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 + content: `<a class="autodevops-link" href="${this.autoDevopsHelpPath}" target="_blank" rel="noopener noreferrer nofollow"> Learn more about Auto DevOps </a>`, - }; - }, + }; }, - }; + }, +}; </script> <template> <div class="table-section section-15 d-none d-sm-none d-md-block pipeline-tags"> diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue index b31b4bad7a0..c9d2dc3a3c5 100644 --- a/app/assets/javascripts/pipelines/components/pipelines.vue +++ b/app/assets/javascripts/pipelines/components/pipelines.vue @@ -1,283 +1,283 @@ <script> - import _ from 'underscore'; - import { __, sprintf, s__ } from '../../locale'; - import createFlash from '../../flash'; - import PipelinesService from '../services/pipelines_service'; - import pipelinesMixin from '../mixins/pipelines'; - import TablePagination from '../../vue_shared/components/table_pagination.vue'; - import NavigationTabs from '../../vue_shared/components/navigation_tabs.vue'; - import NavigationControls from './nav_controls.vue'; - import { getParameterByName } from '../../lib/utils/common_utils'; - import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin'; +import _ from 'underscore'; +import { __, sprintf, s__ } from '../../locale'; +import createFlash from '../../flash'; +import PipelinesService from '../services/pipelines_service'; +import pipelinesMixin from '../mixins/pipelines'; +import TablePagination from '../../vue_shared/components/table_pagination.vue'; +import NavigationTabs from '../../vue_shared/components/navigation_tabs.vue'; +import NavigationControls from './nav_controls.vue'; +import { getParameterByName } from '../../lib/utils/common_utils'; +import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin'; - export default { - components: { - TablePagination, - NavigationTabs, - NavigationControls, +export default { + components: { + TablePagination, + NavigationTabs, + NavigationControls, + }, + mixins: [pipelinesMixin, CIPaginationMixin], + props: { + store: { + type: Object, + required: true, }, - mixins: [pipelinesMixin, CIPaginationMixin], - props: { - store: { - type: Object, - required: true, - }, - // Can be rendered in 3 different places, with some visual differences - // Accepts root | child - // `root` -> main view - // `child` -> rendered inside MR or Commit View - viewType: { - type: String, - required: false, - default: 'root', - }, - endpoint: { - type: String, - required: true, - }, - helpPagePath: { - type: String, - required: true, - }, - emptyStateSvgPath: { - type: String, - required: true, - }, - errorStateSvgPath: { - type: String, - required: true, - }, - noPipelinesSvgPath: { - type: String, - required: true, - }, - autoDevopsPath: { - type: String, - required: true, - }, - hasGitlabCi: { - type: Boolean, - required: true, - }, - canCreatePipeline: { - type: Boolean, - required: true, - }, - ciLintPath: { - type: String, - required: false, - default: null, - }, - resetCachePath: { - type: String, - required: false, - default: null, - }, - newPipelinePath: { - type: String, - required: false, - default: null, - }, + // Can be rendered in 3 different places, with some visual differences + // Accepts root | child + // `root` -> main view + // `child` -> rendered inside MR or Commit View + viewType: { + type: String, + required: false, + default: 'root', }, - data() { - return { - // Start with loading state to avoid a glitch when the empty state will be rendered - isLoading: true, - state: this.store.state, - scope: getParameterByName('scope') || 'all', - page: getParameterByName('page') || '1', - requestData: {}, - isResetCacheButtonLoading: false, - }; + endpoint: { + type: String, + required: true, }, - stateMap: { - // with tabs - loading: 'loading', - tableList: 'tableList', - error: 'error', - emptyTab: 'emptyTab', - - // without tabs - emptyState: 'emptyState', + helpPagePath: { + type: String, + required: true, + }, + emptyStateSvgPath: { + type: String, + required: true, + }, + errorStateSvgPath: { + type: String, + required: true, + }, + noPipelinesSvgPath: { + type: String, + required: true, + }, + autoDevopsPath: { + type: String, + required: true, + }, + hasGitlabCi: { + type: Boolean, + required: true, + }, + canCreatePipeline: { + type: Boolean, + required: true, + }, + ciLintPath: { + type: String, + required: false, + default: null, }, - scopes: { - all: 'all', - pending: 'pending', - running: 'running', - finished: 'finished', - branches: 'branches', - tags: 'tags', + resetCachePath: { + type: String, + required: false, + default: null, }, - computed: { - /** - * `hasGitlabCi` handles both internal and external CI. - * The order on which the checks are made in this method is - * important to guarantee we handle all the corner cases. - */ - stateToRender() { - const { stateMap } = this.$options; + newPipelinePath: { + type: String, + required: false, + default: null, + }, + }, + data() { + return { + // Start with loading state to avoid a glitch when the empty state will be rendered + isLoading: true, + state: this.store.state, + scope: getParameterByName('scope') || 'all', + page: getParameterByName('page') || '1', + requestData: {}, + isResetCacheButtonLoading: false, + }; + }, + stateMap: { + // with tabs + loading: 'loading', + tableList: 'tableList', + error: 'error', + emptyTab: 'emptyTab', + + // without tabs + emptyState: 'emptyState', + }, + scopes: { + all: 'all', + pending: 'pending', + running: 'running', + finished: 'finished', + branches: 'branches', + tags: 'tags', + }, + computed: { + /** + * `hasGitlabCi` handles both internal and external CI. + * The order on which the checks are made in this method is + * important to guarantee we handle all the corner cases. + */ + stateToRender() { + const { stateMap } = this.$options; - if (this.isLoading) { - return stateMap.loading; - } + if (this.isLoading) { + return stateMap.loading; + } - if (this.hasError) { - return stateMap.error; - } + if (this.hasError) { + return stateMap.error; + } - if (this.state.pipelines.length) { - return stateMap.tableList; - } + if (this.state.pipelines.length) { + return stateMap.tableList; + } - if ((this.scope !== 'all' && this.scope !== null) || this.hasGitlabCi) { - return stateMap.emptyTab; - } + if ((this.scope !== 'all' && this.scope !== null) || this.hasGitlabCi) { + return stateMap.emptyTab; + } - return stateMap.emptyState; - }, - /** - * Tabs are rendered in all states except empty state. - * They are not rendered before the first request to avoid a flicker on first load. - */ - shouldRenderTabs() { - const { stateMap } = this.$options; - return ( - this.hasMadeRequest && - [stateMap.loading, stateMap.tableList, stateMap.error, stateMap.emptyTab].includes( - this.stateToRender, - ) - ); - }, + return stateMap.emptyState; + }, + /** + * Tabs are rendered in all states except empty state. + * They are not rendered before the first request to avoid a flicker on first load. + */ + shouldRenderTabs() { + const { stateMap } = this.$options; + return ( + this.hasMadeRequest && + [stateMap.loading, stateMap.tableList, stateMap.error, stateMap.emptyTab].includes( + this.stateToRender, + ) + ); + }, - shouldRenderButtons() { - return ( - (this.newPipelinePath || this.resetCachePath || this.ciLintPath) && this.shouldRenderTabs - ); - }, + shouldRenderButtons() { + return ( + (this.newPipelinePath || this.resetCachePath || this.ciLintPath) && this.shouldRenderTabs + ); + }, - shouldRenderPagination() { - return ( - !this.isLoading && - this.state.pipelines.length && - this.state.pageInfo.total > this.state.pageInfo.perPage - ); - }, + shouldRenderPagination() { + return ( + !this.isLoading && + this.state.pipelines.length && + this.state.pageInfo.total > this.state.pageInfo.perPage + ); + }, - emptyTabMessage() { - const { scopes } = this.$options; - const possibleScopes = [scopes.pending, scopes.running, scopes.finished]; + emptyTabMessage() { + const { scopes } = this.$options; + const possibleScopes = [scopes.pending, scopes.running, scopes.finished]; - if (possibleScopes.includes(this.scope)) { - return sprintf(s__('Pipelines|There are currently no %{scope} pipelines.'), { - scope: this.scope, - }); - } + if (possibleScopes.includes(this.scope)) { + return sprintf(s__('Pipelines|There are currently no %{scope} pipelines.'), { + scope: this.scope, + }); + } - return s__('Pipelines|There are currently no pipelines.'); - }, + return s__('Pipelines|There are currently no pipelines.'); + }, - tabs() { - const { count } = this.state; - const { scopes } = this.$options; + tabs() { + const { count } = this.state; + const { scopes } = this.$options; - return [ - { - name: __('All'), - scope: scopes.all, - count: count.all, - isActive: this.scope === 'all', - }, - { - name: __('Pending'), - scope: scopes.pending, - count: count.pending, - isActive: this.scope === 'pending', - }, - { - name: __('Running'), - scope: scopes.running, - count: count.running, - isActive: this.scope === 'running', - }, - { - name: __('Finished'), - scope: scopes.finished, - count: count.finished, - isActive: this.scope === 'finished', - }, - { - name: __('Branches'), - scope: scopes.branches, - isActive: this.scope === 'branches', - }, - { - name: __('Tags'), - scope: scopes.tags, - isActive: this.scope === 'tags', - }, - ]; - }, + return [ + { + name: __('All'), + scope: scopes.all, + count: count.all, + isActive: this.scope === 'all', + }, + { + name: __('Pending'), + scope: scopes.pending, + count: count.pending, + isActive: this.scope === 'pending', + }, + { + name: __('Running'), + scope: scopes.running, + count: count.running, + isActive: this.scope === 'running', + }, + { + name: __('Finished'), + scope: scopes.finished, + count: count.finished, + isActive: this.scope === 'finished', + }, + { + name: __('Branches'), + scope: scopes.branches, + isActive: this.scope === 'branches', + }, + { + name: __('Tags'), + scope: scopes.tags, + isActive: this.scope === 'tags', + }, + ]; }, - created() { - this.service = new PipelinesService(this.endpoint); - this.requestData = { page: this.page, scope: this.scope }; + }, + created() { + this.service = new PipelinesService(this.endpoint); + this.requestData = { page: this.page, scope: this.scope }; + }, + methods: { + successCallback(resp) { + // Because we are polling & the user is interacting verify if the response received + // matches the last request made + if (_.isEqual(resp.config.params, this.requestData)) { + this.store.storeCount(resp.data.count); + this.store.storePagination(resp.headers); + this.setCommonData(resp.data.pipelines); + } }, - methods: { - successCallback(resp) { - // Because we are polling & the user is interacting verify if the response received - // matches the last request made - if (_.isEqual(resp.config.params, this.requestData)) { - this.store.storeCount(resp.data.count); - this.store.storePagination(resp.headers); - this.setCommonData(resp.data.pipelines); - } - }, - /** - * Handles URL and query parameter changes. - * When the user uses the pagination or the tabs, - * - update URL - * - Make API request to the server with new parameters - * - Update the polling function - * - Update the internal state - */ - updateContent(parameters) { - this.updateInternalState(parameters); + /** + * Handles URL and query parameter changes. + * When the user uses the pagination or the tabs, + * - update URL + * - Make API request to the server with new parameters + * - Update the polling function + * - Update the internal state + */ + updateContent(parameters) { + this.updateInternalState(parameters); - // fetch new data - return this.service - .getPipelines(this.requestData) - .then(response => { - this.isLoading = false; - this.successCallback(response); + // fetch new data + return this.service + .getPipelines(this.requestData) + .then(response => { + this.isLoading = false; + this.successCallback(response); - // restart polling - this.poll.restart({ data: this.requestData }); - }) - .catch(() => { - this.isLoading = false; - this.errorCallback(); + // restart polling + this.poll.restart({ data: this.requestData }); + }) + .catch(() => { + this.isLoading = false; + this.errorCallback(); - // restart polling - this.poll.restart({ data: this.requestData }); - }); - }, + // restart polling + this.poll.restart({ data: this.requestData }); + }); + }, - handleResetRunnersCache(endpoint) { - this.isResetCacheButtonLoading = true; + handleResetRunnersCache(endpoint) { + this.isResetCacheButtonLoading = true; - this.service - .postAction(endpoint) - .then(() => { - this.isResetCacheButtonLoading = false; - createFlash(s__('Pipelines|Project cache successfully reset.'), 'notice'); - }) - .catch(() => { - this.isResetCacheButtonLoading = false; - createFlash(s__('Pipelines|Something went wrong while cleaning runners cache.')); - }); - }, + this.service + .postAction(endpoint) + .then(() => { + this.isResetCacheButtonLoading = false; + createFlash(s__('Pipelines|Project cache successfully reset.'), 'notice'); + }) + .catch(() => { + this.isResetCacheButtonLoading = false; + createFlash(s__('Pipelines|Something went wrong while cleaning runners cache.')); + }); }, - }; + }, +}; </script> <template> <div class="pipelines-container"> diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue index 5070c253f11..1c8d7303c52 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue @@ -1,44 +1,44 @@ <script> - import eventHub from '../event_hub'; - import loadingIcon from '../../vue_shared/components/loading_icon.vue'; - import icon from '../../vue_shared/components/icon.vue'; - import tooltip from '../../vue_shared/directives/tooltip'; +import eventHub from '../event_hub'; +import loadingIcon from '../../vue_shared/components/loading_icon.vue'; +import icon from '../../vue_shared/components/icon.vue'; +import tooltip from '../../vue_shared/directives/tooltip'; - export default { - directives: { - tooltip, +export default { + directives: { + tooltip, + }, + components: { + loadingIcon, + icon, + }, + props: { + actions: { + type: Array, + required: true, }, - components: { - loadingIcon, - icon, - }, - props: { - actions: { - type: Array, - required: true, - }, - }, - data() { - return { - isLoading: false, - }; - }, - methods: { - onClickAction(endpoint) { - this.isLoading = true; + }, + data() { + return { + isLoading: false, + }; + }, + methods: { + onClickAction(endpoint) { + this.isLoading = true; - eventHub.$emit('postAction', endpoint); - }, + eventHub.$emit('postAction', endpoint); + }, - isActionDisabled(action) { - if (action.playable === undefined) { - return false; - } + isActionDisabled(action) { + if (action.playable === undefined) { + return false; + } - return !action.playable; - }, + return !action.playable; }, - }; + }, +}; </script> <template> <div class="btn-group"> diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue index 490df47e154..d40de95e051 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue @@ -1,21 +1,21 @@ <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 { - directives: { - tooltip, +export default { + directives: { + tooltip, + }, + components: { + icon, + }, + props: { + artifacts: { + type: Array, + required: true, }, - components: { - icon, - }, - props: { - artifacts: { - type: Array, - required: true, - }, - }, - }; + }, +}; </script> <template> <div diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue index 2e777783636..0d7324f3fb5 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue @@ -1,74 +1,82 @@ <script> - import Modal from '~/vue_shared/components/gl_modal.vue'; - import { s__, sprintf } from '~/locale'; - import PipelinesTableRowComponent from './pipelines_table_row.vue'; - import eventHub from '../event_hub'; +import Modal from '~/vue_shared/components/gl_modal.vue'; +import { s__, sprintf } from '~/locale'; +import PipelinesTableRowComponent from './pipelines_table_row.vue'; +import eventHub from '../event_hub'; - /** - * Pipelines Table Component. - * - * Given an array of objects, renders a table. - */ - export default { - components: { - PipelinesTableRowComponent, - Modal, +/** + * Pipelines Table Component. + * + * Given an array of objects, renders a table. + */ +export default { + components: { + PipelinesTableRowComponent, + Modal, + }, + props: { + pipelines: { + type: Array, + required: true, }, - props: { - pipelines: { - type: Array, - required: true, - }, - updateGraphDropdown: { - type: Boolean, - required: false, - default: false, - }, - autoDevopsHelpPath: { - type: String, - required: true, - }, - viewType: { - type: String, - required: true, - }, + updateGraphDropdown: { + type: Boolean, + required: false, + default: false, }, - data() { - return { - pipelineId: '', - endpoint: '', - cancelingPipeline: null, - }; + autoDevopsHelpPath: { + type: String, + required: true, }, - computed: { - modalTitle() { - return sprintf(s__('Pipeline|Stop pipeline #%{pipelineId}?'), { + viewType: { + type: String, + required: true, + }, + }, + data() { + return { + pipelineId: '', + endpoint: '', + cancelingPipeline: null, + }; + }, + computed: { + modalTitle() { + return sprintf( + s__('Pipeline|Stop pipeline #%{pipelineId}?'), + { pipelineId: `${this.pipelineId}`, - }, false); - }, - modalText() { - return sprintf(s__('Pipeline|You’re about to stop pipeline %{pipelineId}.'), { - pipelineId: `<strong>#${this.pipelineId}</strong>`, - }, false); - }, + }, + false, + ); }, - created() { - eventHub.$on('openConfirmationModal', this.setModalData); + modalText() { + return sprintf( + s__('Pipeline|You’re about to stop pipeline %{pipelineId}.'), + { + pipelineId: `<strong>#${this.pipelineId}</strong>`, + }, + false, + ); }, - beforeDestroy() { - eventHub.$off('openConfirmationModal', this.setModalData); + }, + created() { + eventHub.$on('openConfirmationModal', this.setModalData); + }, + beforeDestroy() { + eventHub.$off('openConfirmationModal', this.setModalData); + }, + methods: { + setModalData(data) { + this.pipelineId = data.pipelineId; + this.endpoint = data.endpoint; }, - methods: { - setModalData(data) { - this.pipelineId = data.pipelineId; - this.endpoint = data.endpoint; - }, - onSubmit() { - eventHub.$emit('postAction', this.endpoint); - this.cancelingPipeline = this.pipelineId; - }, + onSubmit() { + eventHub.$emit('postAction', this.endpoint); + this.cancelingPipeline = this.pipelineId; }, - }; + }, +}; </script> <template> <div class="ci-table"> diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue index b2744a30c2a..804822a3ea8 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue @@ -1,255 +1,253 @@ <script> - import eventHub from '../event_hub'; - import PipelinesActionsComponent from './pipelines_actions.vue'; - import PipelinesArtifactsComponent from './pipelines_artifacts.vue'; - import CiBadge from '../../vue_shared/components/ci_badge_link.vue'; - import PipelineStage from './stage.vue'; - import PipelineUrl from './pipeline_url.vue'; - import PipelinesTimeago from './time_ago.vue'; - import CommitComponent from '../../vue_shared/components/commit.vue'; - import LoadingButton from '../../vue_shared/components/loading_button.vue'; - import Icon from '../../vue_shared/components/icon.vue'; - import { PIPELINES_TABLE } from '../constants'; +import eventHub from '../event_hub'; +import PipelinesActionsComponent from './pipelines_actions.vue'; +import PipelinesArtifactsComponent from './pipelines_artifacts.vue'; +import CiBadge from '../../vue_shared/components/ci_badge_link.vue'; +import PipelineStage from './stage.vue'; +import PipelineUrl from './pipeline_url.vue'; +import PipelinesTimeago from './time_ago.vue'; +import CommitComponent from '../../vue_shared/components/commit.vue'; +import LoadingButton from '../../vue_shared/components/loading_button.vue'; +import Icon from '../../vue_shared/components/icon.vue'; +import { PIPELINES_TABLE } from '../constants'; - /** - * Pipeline table row. - * - * Given the received object renders a table row in the pipelines' table. - */ - export default { - components: { - PipelinesActionsComponent, - PipelinesArtifactsComponent, - CommitComponent, - PipelineStage, - PipelineUrl, - CiBadge, - PipelinesTimeago, - LoadingButton, - Icon, +/** + * Pipeline table row. + * + * Given the received object renders a table row in the pipelines' table. + */ +export default { + components: { + PipelinesActionsComponent, + PipelinesArtifactsComponent, + CommitComponent, + PipelineStage, + PipelineUrl, + CiBadge, + PipelinesTimeago, + LoadingButton, + Icon, + }, + props: { + pipeline: { + type: Object, + required: true, }, - props: { - pipeline: { - type: Object, - required: true, - }, - updateGraphDropdown: { - type: Boolean, - required: false, - default: false, - }, - autoDevopsHelpPath: { - type: String, - required: true, - }, - viewType: { - type: String, - required: true, - }, - cancelingPipeline: { - type: String, - required: false, - default: null, - }, + updateGraphDropdown: { + type: Boolean, + required: false, + default: false, }, - pipelinesTable: PIPELINES_TABLE, - data() { - return { - isRetrying: false, - }; + autoDevopsHelpPath: { + type: String, + required: true, }, - computed: { - /** - * If provided, returns the commit tag. - * Needed to render the commit component column. - * - * This field needs a lot of verification, because of different possible cases: - * - * 1. person who is an author of a commit might be a GitLab user - * 2. if person who is an author of a commit is a GitLab user he/she can have a GitLab avatar - * 3. If GitLab user does not have avatar he/she might have a Gravatar - * 4. If committer is not a GitLab User he/she can have a Gravatar - * 5. We do not have consistent API object in this case - * 6. We should improve API and the code - * - * @returns {Object|Undefined} - */ - commitAuthor() { - let commitAuthorInformation; + viewType: { + type: String, + required: true, + }, + cancelingPipeline: { + type: String, + required: false, + default: null, + }, + }, + pipelinesTable: PIPELINES_TABLE, + data() { + return { + isRetrying: false, + }; + }, + computed: { + /** + * If provided, returns the commit tag. + * Needed to render the commit component column. + * + * This field needs a lot of verification, because of different possible cases: + * + * 1. person who is an author of a commit might be a GitLab user + * 2. if person who is an author of a commit is a GitLab user he/she can have a GitLab avatar + * 3. If GitLab user does not have avatar he/she might have a Gravatar + * 4. If committer is not a GitLab User he/she can have a Gravatar + * 5. We do not have consistent API object in this case + * 6. We should improve API and the code + * + * @returns {Object|Undefined} + */ + commitAuthor() { + let commitAuthorInformation; - if (!this.pipeline || !this.pipeline.commit) { - return null; - } + if (!this.pipeline || !this.pipeline.commit) { + return null; + } - // 1. person who is an author of a commit might be a GitLab user - if (this.pipeline.commit.author) { - // 2. if person who is an author of a commit is a GitLab user - // he/she can have a GitLab avatar - if (this.pipeline.commit.author.avatar_url) { - commitAuthorInformation = this.pipeline.commit.author; + // 1. person who is an author of a commit might be a GitLab user + if (this.pipeline.commit.author) { + // 2. if person who is an author of a commit is a GitLab user + // he/she can have a GitLab avatar + if (this.pipeline.commit.author.avatar_url) { + commitAuthorInformation = this.pipeline.commit.author; - // 3. If GitLab user does not have avatar he/she might have a Gravatar - } else if (this.pipeline.commit.author_gravatar_url) { - commitAuthorInformation = Object.assign({}, this.pipeline.commit.author, { - avatar_url: this.pipeline.commit.author_gravatar_url, - }); - } - // 4. If committer is not a GitLab User he/she can have a Gravatar - } else { - commitAuthorInformation = { + // 3. If GitLab user does not have avatar he/she might have a Gravatar + } else if (this.pipeline.commit.author_gravatar_url) { + commitAuthorInformation = Object.assign({}, this.pipeline.commit.author, { avatar_url: this.pipeline.commit.author_gravatar_url, - path: `mailto:${this.pipeline.commit.author_email}`, - username: this.pipeline.commit.author_name, - }; + }); } + // 4. If committer is not a GitLab User he/she can have a Gravatar + } else { + commitAuthorInformation = { + avatar_url: this.pipeline.commit.author_gravatar_url, + path: `mailto:${this.pipeline.commit.author_email}`, + username: this.pipeline.commit.author_name, + }; + } - return commitAuthorInformation; - }, + return commitAuthorInformation; + }, - /** - * If provided, returns the commit tag. - * Needed to render the commit component column. - * - * @returns {String|Undefined} - */ - commitTag() { - if (this.pipeline.ref && - this.pipeline.ref.tag) { - return this.pipeline.ref.tag; - } - return undefined; - }, + /** + * If provided, returns the commit tag. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitTag() { + if (this.pipeline.ref && this.pipeline.ref.tag) { + return this.pipeline.ref.tag; + } + return undefined; + }, - /** - * If provided, returns the commit ref. - * Needed to render the commit component column. - * - * Matches `path` prop sent in the API to `ref_url` prop needed - * in the commit component. - * - * @returns {Object|Undefined} - */ - commitRef() { - if (this.pipeline.ref) { - return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => { - if (prop === 'path') { - // eslint-disable-next-line no-param-reassign - accumulator.ref_url = this.pipeline.ref[prop]; - } else { - // eslint-disable-next-line no-param-reassign - accumulator[prop] = this.pipeline.ref[prop]; - } - return accumulator; - }, {}); - } + /** + * If provided, returns the commit ref. + * Needed to render the commit component column. + * + * Matches `path` prop sent in the API to `ref_url` prop needed + * in the commit component. + * + * @returns {Object|Undefined} + */ + commitRef() { + if (this.pipeline.ref) { + return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => { + if (prop === 'path') { + // eslint-disable-next-line no-param-reassign + accumulator.ref_url = this.pipeline.ref[prop]; + } else { + // eslint-disable-next-line no-param-reassign + accumulator[prop] = this.pipeline.ref[prop]; + } + return accumulator; + }, {}); + } - return undefined; - }, + return undefined; + }, - /** - * If provided, returns the commit url. - * Needed to render the commit component column. - * - * @returns {String|Undefined} - */ - commitUrl() { - if (this.pipeline.commit && - this.pipeline.commit.commit_path) { - return this.pipeline.commit.commit_path; - } - return undefined; - }, + /** + * If provided, returns the commit url. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitUrl() { + if (this.pipeline.commit && this.pipeline.commit.commit_path) { + return this.pipeline.commit.commit_path; + } + return undefined; + }, - /** - * If provided, returns the commit short sha. - * Needed to render the commit component column. - * - * @returns {String|Undefined} - */ - commitShortSha() { - if (this.pipeline.commit && - this.pipeline.commit.short_id) { - return this.pipeline.commit.short_id; - } - return undefined; - }, + /** + * If provided, returns the commit short sha. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitShortSha() { + if (this.pipeline.commit && this.pipeline.commit.short_id) { + return this.pipeline.commit.short_id; + } + return undefined; + }, - /** - * If provided, returns the commit title. - * Needed to render the commit component column. - * - * @returns {String|Undefined} - */ - commitTitle() { - if (this.pipeline.commit && - this.pipeline.commit.title) { - return this.pipeline.commit.title; - } - return undefined; - }, + /** + * If provided, returns the commit title. + * Needed to render the commit component column. + * + * @returns {String|Undefined} + */ + commitTitle() { + if (this.pipeline.commit && this.pipeline.commit.title) { + return this.pipeline.commit.title; + } + return undefined; + }, - /** - * Timeago components expects a number - * - * @return {type} description - */ - pipelineDuration() { - if (this.pipeline.details && this.pipeline.details.duration) { - return this.pipeline.details.duration; - } + /** + * Timeago components expects a number + * + * @return {type} description + */ + pipelineDuration() { + if (this.pipeline.details && this.pipeline.details.duration) { + return this.pipeline.details.duration; + } - return 0; - }, + return 0; + }, - /** - * Timeago component expects a String. - * - * @return {String} - */ - pipelineFinishedAt() { - if (this.pipeline.details && this.pipeline.details.finished_at) { - return this.pipeline.details.finished_at; - } + /** + * Timeago component expects a String. + * + * @return {String} + */ + pipelineFinishedAt() { + if (this.pipeline.details && this.pipeline.details.finished_at) { + return this.pipeline.details.finished_at; + } - return ''; - }, + return ''; + }, - pipelineStatus() { - if (this.pipeline.details && this.pipeline.details.status) { - return this.pipeline.details.status; - } - return {}; - }, + pipelineStatus() { + if (this.pipeline.details && this.pipeline.details.status) { + return this.pipeline.details.status; + } + return {}; + }, - displayPipelineActions() { - return this.pipeline.flags.retryable || - this.pipeline.flags.cancelable || - this.pipeline.details.manual_actions.length || - this.pipeline.details.artifacts.length; - }, + displayPipelineActions() { + return ( + this.pipeline.flags.retryable || + this.pipeline.flags.cancelable || + this.pipeline.details.manual_actions.length || + this.pipeline.details.artifacts.length + ); + }, - isChildView() { - return this.viewType === 'child'; - }, + isChildView() { + return this.viewType === 'child'; + }, - isCancelling() { - return this.cancelingPipeline === this.pipeline.id; - }, + isCancelling() { + return this.cancelingPipeline === this.pipeline.id; }, + }, - methods: { - handleCancelClick() { - eventHub.$emit('openConfirmationModal', { - pipelineId: this.pipeline.id, - endpoint: this.pipeline.cancel_path, - }); - }, - handleRetryClick() { - this.isRetrying = true; - eventHub.$emit('retryPipeline', this.pipeline.retry_path); - }, + methods: { + handleCancelClick() { + eventHub.$emit('openConfirmationModal', { + pipelineId: this.pipeline.id, + endpoint: this.pipeline.cancel_path, + }); + }, + handleRetryClick() { + this.isRetrying = true; + eventHub.$emit('retryPipeline', this.pipeline.retry_path); }, - }; + }, +}; </script> <template> <div class="commit gl-responsive-table-row"> diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index b9231c002fd..56fdb858088 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -186,32 +186,27 @@ export default { </i> </button> - <ul + <div class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container" aria-labelledby="stageDropdown" > - - <li + <loading-icon v-if="isLoading"/> + <ul + v-else class="js-builds-dropdown-list scrollable-menu" > - - <loading-icon v-if="isLoading"/> - - <ul - v-else + <li + v-for="job in dropdownContent" + :key="job.id" > - <li - v-for="job in dropdownContent" - :key="job.id" - > - <job-component - :job="job" - css-class-job-name="mini-pipeline-graph-dropdown-item" - @pipelineActionRequestComplete="pipelineActionRequestComplete" - /> - </li> - </ul> - </li> - </ul> + <job-component + :dropdown-length="dropdownContent.length" + :job="job" + css-class-job-name="mini-pipeline-graph-dropdown-item" + @pipelineActionRequestComplete="pipelineActionRequestComplete" + /> + </li> + </ul> + </div> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/time_ago.vue b/app/assets/javascripts/pipelines/components/time_ago.vue index 0a97df2dc18..cd43d78de40 100644 --- a/app/assets/javascripts/pipelines/components/time_ago.vue +++ b/app/assets/javascripts/pipelines/components/time_ago.vue @@ -1,60 +1,58 @@ <script> - import iconTimerSvg from 'icons/_icon_timer.svg'; - import '../../lib/utils/datetime_utility'; - import tooltip from '../../vue_shared/directives/tooltip'; - import timeagoMixin from '../../vue_shared/mixins/timeago'; +import iconTimerSvg from 'icons/_icon_timer.svg'; +import '../../lib/utils/datetime_utility'; +import tooltip from '../../vue_shared/directives/tooltip'; +import timeagoMixin from '../../vue_shared/mixins/timeago'; - export default { - directives: { - tooltip, +export default { + directives: { + tooltip, + }, + mixins: [timeagoMixin], + props: { + finishedTime: { + type: String, + required: true, }, - mixins: [ - timeagoMixin, - ], - props: { - finishedTime: { - type: String, - required: true, - }, - duration: { - type: Number, - required: true, - }, + duration: { + type: Number, + required: true, }, - data() { - return { - iconTimerSvg, - }; + }, + data() { + return { + iconTimerSvg, + }; + }, + computed: { + hasDuration() { + return this.duration > 0; }, - computed: { - hasDuration() { - return this.duration > 0; - }, - hasFinishedTime() { - return this.finishedTime !== ''; - }, - durationFormated() { - const date = new Date(this.duration * 1000); + hasFinishedTime() { + return this.finishedTime !== ''; + }, + durationFormated() { + const date = new Date(this.duration * 1000); - let hh = date.getUTCHours(); - let mm = date.getUTCMinutes(); - let ss = date.getSeconds(); + let hh = date.getUTCHours(); + let mm = date.getUTCMinutes(); + let ss = date.getSeconds(); - // left pad - if (hh < 10) { - hh = `0${hh}`; - } - if (mm < 10) { - mm = `0${mm}`; - } - if (ss < 10) { - ss = `0${ss}`; - } + // left pad + if (hh < 10) { + hh = `0${hh}`; + } + if (mm < 10) { + mm = `0${mm}`; + } + if (ss < 10) { + ss = `0${ss}`; + } - return `${hh}:${mm}:${ss}`; - }, + return `${hh}:${mm}:${ss}`; }, - }; + }, +}; </script> <template> <div class="table-section section-15 pipelines-time-ago"> |