From 34e317d21ce49c122beb73d2eb695311b75d0b89 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 19 Dec 2016 17:58:46 +0000 Subject: [PATCH] Adds loading to improve UX --- .../mini_pipeline_graph_dropdown.js.es6 | 42 ++++++++++++++----- app/assets/stylesheets/pages/pipelines.scss | 12 ++++-- .../projects/ci/pipelines/_pipeline.html.haml | 10 ++++- app/views/projects/pipelines/_stage.html.haml | 10 ++--- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 index ce24cbdb705..1db1ad6f017 100644 --- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 +++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js.es6 @@ -18,31 +18,34 @@ class MiniPipelineGraph { constructor({ container }) { this.container = container; + this.dropdownListSelector = '.js-builds-dropdown-container'; this.getBuildsList = this.getBuildsList.bind(this); this.bindEvents(); } /** - * Adds an removes the event listener. - * TODO: Remove jQuery when we have a way to handle events properly. + * Adds and removes the event listener. */ bindEvents() { - $(this.container).off('click', 'button.js-builds-dropdown-button', this.getBuildsList); - $(this.container).on('click', 'button.js-builds-dropdown-button', this.getBuildsList); + const dropdownButtonSelector = 'button.js-builds-dropdown-button'; + + $(this.container).off('click', dropdownButtonSelector, this.getBuildsList); + $(this.container).on('click', dropdownButtonSelector, this.getBuildsList); } /** - * For the clicked stage, renders the received html in the sibiling - * element with the `js-builds-dropdown-container` clas + * For the clicked stage, renders the given data in the dropdown list. * - * @param {Element} stageContainer + * @param {HTMLElement} stageContainer * @param {Object} data */ renderBuildsList(stageContainer, data) { - const dropdownContainer = stageContainer.parentElement.querySelector('.js-builds-dropdown-container'); + const dropdownContainer = stageContainer.parentElement.querySelector( + `${this.dropdownListSelector} .js-builds-dropdown-list`, + ); - dropdownContainer.innerHTML = data.html; + dropdownContainer.innerHTML = data; } /** @@ -58,10 +61,29 @@ dataType: 'json', type: 'GET', url: endpoint, - success: data => this.renderBuildsList(e.currentTarget, data), + beforeSend: () => { + this.renderBuildsList(e.currentTarget, ''); + this.toggleLoading(e.currentTarget); + }, + success: (data) => { + this.toggleLoading(e.currentTarget); + this.renderBuildsList(e.currentTarget, data.html); + }, error: () => new Flash('An error occurred while fetching the builds.', 'alert'), }); } + + /** + * Toggles the visibility of the loading icon. + * + * @param {HTMLElement} stageContainer + * @return {type} + */ + toggleLoading(stageContainer) { + stageContainer.parentElement.querySelector( + `${this.dropdownListSelector} .js-builds-dropdown-loading`, + ).classList.toggle('hidden'); + } } window.gl = window.gl || {}; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index b3deac3ab75..ae7b40a9416 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -753,13 +753,19 @@ margin: 0; } + .builds-dropdown-loading { + margin: 10px auto; + width: 18px; + } + .grouped-pipeline-dropdown { right: -172px; top: 23px; - } + min-height: 191px; - .grouped-pipeline-dropdown a { - color: $gl-text-color-light; + a { + color: $gl-text-color-light; + } } .arrow-up { diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index d488eeda2fe..6dfc55aa23c 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -53,13 +53,19 @@ .stage-container.mini-pipeline-graph - if hasMultipleBuilds .dropdown.inline.build-content - %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: tooltip, "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} + %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: tooltip, placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} %span{ class: klass } %span.mini-pipeline-graph-icon-container %span{ class: icon_status_klass }= custom_icon(icon_status) = icon('caret-down', class: 'dropdown-caret') - %div.js-builds-dropdown-container + .js-builds-dropdown-container + .dropdown-menu.grouped-pipeline-dropdown + .arrow-up + .js-builds-dropdown-list + + .js-builds-dropdown-loading.builds-dropdown-loading.hidden + %span.fa.fa-spinner.fa-spin - else - if detailed_status.has_details? = link_to detailed_status.details_path, class: klass, title: tooltip do diff --git a/app/views/projects/pipelines/_stage.html.haml b/app/views/projects/pipelines/_stage.html.haml index 83fd518726d..20456e792e7 100644 --- a/app/views/projects/pipelines/_stage.html.haml +++ b/app/views/projects/pipelines/_stage.html.haml @@ -1,6 +1,4 @@ -.dropdown-menu.grouped-pipeline-dropdown - .arrow-up - %ul - - @stage.statuses.each do |status| - %li.dropdown-build - = render 'ci/status/graph_badge', subject: status +%ul + - @stage.statuses.each do |status| + %li.dropdown-build + = render 'ci/status/graph_badge', subject: status