diff options
author | Phil Hughes <me@iamphill.com> | 2017-05-04 13:11:50 +0000 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-05-04 13:11:50 +0000 |
commit | 9eab1db9a2ec8832e50bbb862408bd7fac7aa421 (patch) | |
tree | f315cea436799b8eb235136b2dc82003dfd703bb /app/assets/javascripts/pipelines | |
parent | dcdced81ae249d38837068d2cc6b7e21ebf1bef2 (diff) | |
parent | e69732e2fb4a2a22947bb6faadd0292275c03ace (diff) | |
download | gitlab-ce-9eab1db9a2ec8832e50bbb862408bd7fac7aa421.tar.gz |
Merge branch '31558-job-dropdown' into 'master'
Pipeline table mini graph dropdown remains open when table is refreshed
Closes #31558 and #31433
See merge request !11033
Diffstat (limited to 'app/assets/javascripts/pipelines')
-rw-r--r-- | app/assets/javascripts/pipelines/components/stage.js | 115 | ||||
-rw-r--r-- | app/assets/javascripts/pipelines/components/stage.vue | 173 | ||||
-rw-r--r-- | app/assets/javascripts/pipelines/pipelines.js | 11 |
3 files changed, 183 insertions, 116 deletions
diff --git a/app/assets/javascripts/pipelines/components/stage.js b/app/assets/javascripts/pipelines/components/stage.js deleted file mode 100644 index 203485f2990..00000000000 --- a/app/assets/javascripts/pipelines/components/stage.js +++ /dev/null @@ -1,115 +0,0 @@ -/* global Flash */ -import StatusIconEntityMap from '../../ci_status_icons'; - -export default { - props: { - stage: { - type: Object, - required: true, - }, - }, - - data() { - return { - builds: '', - spinner: '<span class="fa fa-spinner fa-spin"></span>', - }; - }, - - updated() { - if (this.builds) { - this.stopDropdownClickPropagation(); - } - }, - - methods: { - fetchBuilds(e) { - const ariaExpanded = e.currentTarget.attributes['aria-expanded']; - - if (ariaExpanded && (ariaExpanded.textContent === 'true')) return null; - - return this.$http.get(this.stage.dropdown_path) - .then((response) => { - this.builds = JSON.parse(response.body).html; - }) - .catch(() => { - // If dropdown is opened we'll close it. - if (this.$el.classList.contains('open')) { - $(this.$refs.dropdown).dropdown('toggle'); - } - - const flash = new Flash('Something went wrong on our end.'); - return flash; - }); - }, - - /** - * When the user right clicks or cmd/ctrl + click in the job name - * the dropdown should not be closed and the link should open in another tab, - * so we stop propagation of the click event inside the dropdown. - * - * Since this component is rendered multiple times per page we need to guarantee we only - * target the click event of this component. - */ - stopDropdownClickPropagation() { - $(this.$el.querySelectorAll('.js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item')) - .on('click', (e) => { - e.stopPropagation(); - }); - }, - }, - computed: { - buildsOrSpinner() { - return this.builds ? this.builds : this.spinner; - }, - dropdownClass() { - if (this.builds) return 'js-builds-dropdown-container'; - return 'js-builds-dropdown-loading builds-dropdown-loading'; - }, - buildStatus() { - return `Build: ${this.stage.status.label}`; - }, - tooltip() { - return `has-tooltip ci-status-icon ci-status-icon-${this.stage.status.group}`; - }, - triggerButtonClass() { - return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`; - }, - svgHTML() { - return StatusIconEntityMap[this.stage.status.icon]; - }, - }, - template: ` - <div> - <button - @click="fetchBuilds($event)" - :class="triggerButtonClass" - :title="stage.title" - data-placement="top" - data-toggle="dropdown" - type="button" - :aria-label="stage.title" - ref="dropdown"> - <span - v-html="svgHTML" - aria-hidden="true"> - </span> - <i - class="fa fa-caret-down" - aria-hidden="true" /> - </button> - <ul - ref="dropdown-content" - class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"> - <div - class="arrow-up" - aria-hidden="true"></div> - <div - :class="dropdownClass" - class="js-builds-dropdown-list scrollable-menu" - v-html="buildsOrSpinner"> - </div> - </ul> - </div> - `, -}; diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue new file mode 100644 index 00000000000..2e485f951a1 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -0,0 +1,173 @@ +<script> + +/** + * Renders each stage of the pipeline mini graph. + * + * Given the provided endpoint will make a request to + * fetch the dropdown data when the stage is clicked. + * + * Request is made inside this component to make it reusable between: + * 1. Pipelines main table + * 2. Pipelines table in commit and Merge request views + * 3. Merge request widget + * 4. Commit widget + */ + +/* global Flash */ +import StatusIconEntityMap from '../../ci_status_icons'; + +export default { + props: { + stage: { + type: Object, + required: true, + }, + + updateDropdown: { + type: Boolean, + required: false, + default: false, + }, + }, + + data() { + return { + isLoading: false, + dropdownContent: '', + endpoint: this.stage.dropdown_path, + }; + }, + + updated() { + if (this.dropdownContent.length > 0) { + this.stopDropdownClickPropagation(); + } + }, + + watch: { + updateDropdown() { + if (this.updateDropdown && + this.isDropdownOpen() && + !this.isLoading) { + this.fetchJobs(); + } + }, + }, + + methods: { + onClickStage() { + if (!this.isDropdownOpen()) { + this.isLoading = true; + this.fetchJobs(); + } + }, + + fetchJobs() { + this.$http.get(this.endpoint) + .then((response) => { + this.dropdownContent = response.json().html; + this.isLoading = false; + }) + .catch(() => { + this.closeDropdown(); + this.isLoading = false; + + const flash = new Flash('Something went wrong on our end.'); + return flash; + }); + }, + + /** + * When the user right clicks or cmd/ctrl + click in the job name + * the dropdown should not be closed and the link should open in another tab, + * so we stop propagation of the click event inside the dropdown. + * + * Since this component is rendered multiple times per page we need to guarantee we only + * target the click event of this component. + */ + stopDropdownClickPropagation() { + $(this.$el.querySelectorAll('.js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item')) + .on('click', (e) => { + e.stopPropagation(); + }); + }, + + closeDropdown() { + if (this.isDropdownOpen()) { + $(this.$refs.dropdown).dropdown('toggle'); + } + }, + + isDropdownOpen() { + return this.$el.classList.contains('open'); + }, + }, + + computed: { + dropdownClass() { + return this.dropdownContent.length > 0 ? 'js-builds-dropdown-container' : 'js-builds-dropdown-loading'; + }, + + triggerButtonClass() { + return `ci-status-icon-${this.stage.status.group}`; + }, + + svgIcon() { + return StatusIconEntityMap[this.stage.status.icon]; + }, + }, +}; +</script> + +<template> + <div class="dropdown"> + <button + :class="triggerButtonClass" + @click="onClickStage" + class="mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button" + :title="stage.title" + data-placement="top" + data-toggle="dropdown" + type="button" + id="stageDropdown" + aria-haspopup="true" + aria-expanded="false"> + + <span + v-html="svgIcon" + aria-hidden="true" + :aria-label="stage.title"> + </span> + + <i + class="fa fa-caret-down" + aria-hidden="true"> + </i> + </button> + + <ul + class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container" + aria-labelledby="stageDropdown"> + + <li + :class="dropdownClass" + class="js-builds-dropdown-list scrollable-menu"> + + <div + class="text-center" + v-if="isLoading"> + <i + class="fa fa-spin fa-spinner" + aria-hidden="true" + aria-label="Loading"> + </i> + </div> + + <ul + v-else + v-html="dropdownContent"> + </ul> + </li> + </ul> + </div> +</script> diff --git a/app/assets/javascripts/pipelines/pipelines.js b/app/assets/javascripts/pipelines/pipelines.js index 93d4818231f..934bd7deb31 100644 --- a/app/assets/javascripts/pipelines/pipelines.js +++ b/app/assets/javascripts/pipelines/pipelines.js @@ -49,6 +49,7 @@ export default { isLoading: false, hasError: false, isMakingRequest: false, + updateGraphDropdown: false, }; }, @@ -198,15 +199,21 @@ export default { this.store.storePagination(response.headers); this.isLoading = false; + this.updateGraphDropdown = true; }, errorCallback() { this.hasError = true; this.isLoading = false; + this.updateGraphDropdown = false; }, setIsMakingRequest(isMakingRequest) { this.isMakingRequest = isMakingRequest; + + if (isMakingRequest) { + this.updateGraphDropdown = false; + } }, }, @@ -263,7 +270,9 @@ export default { <pipelines-table-component :pipelines="state.pipelines" - :service="service"/> + :service="service" + :update-graph-dropdown="updateGraphDropdown" + /> </div> <gl-pagination |