Commit 5a3b2f83 authored by Filipa Lacerda's avatar Filipa Lacerda

Uses vanilla JS to listen to click event

Removes `:visible` from selector
Adds CHANGELOG entry
Removes iife
parent 429eb466
...@@ -39,6 +39,7 @@ import Issue from './issue'; ...@@ -39,6 +39,7 @@ import Issue from './issue';
import BindInOut from './behaviors/bind_in_out'; import BindInOut from './behaviors/bind_in_out';
import GroupsList from './groups_list'; import GroupsList from './groups_list';
import ProjectsList from './projects_list'; import ProjectsList from './projects_list';
import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
const ShortcutsBlob = require('./shortcuts_blob'); const ShortcutsBlob = require('./shortcuts_blob');
const UserCallout = require('./user_callout'); const UserCallout = require('./user_callout');
...@@ -181,7 +182,7 @@ const UserCallout = require('./user_callout'); ...@@ -181,7 +182,7 @@ const UserCallout = require('./user_callout');
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
break; break;
case 'projects:commit:pipelines': case 'projects:commit:pipelines':
new gl.MiniPipelineGraph({ new MiniPipelineGraph({
container: '.js-pipeline-table', container: '.js-pipeline-table',
}).bindEvents(); }).bindEvents();
break; break;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
/* global merge_request_widget */ /* global merge_request_widget */
require('./smart_interval'); require('./smart_interval');
const MiniPipelineGraph = require('./mini_pipeline_graph_dropdown').default;
((global) => { ((global) => {
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; }; var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; };
...@@ -285,8 +286,8 @@ require('./smart_interval'); ...@@ -285,8 +286,8 @@ require('./smart_interval');
}; };
MergeRequestWidget.prototype.initMiniPipelineGraph = function() { MergeRequestWidget.prototype.initMiniPipelineGraph = function() {
new gl.MiniPipelineGraph({ new MiniPipelineGraph({
container: '.js-pipeline-inline-mr-widget-graph:visible', container: '.js-pipeline-inline-mr-widget-graph',
}).bindEvents(); }).bindEvents();
}; };
......
...@@ -15,97 +15,92 @@ ...@@ -15,97 +15,92 @@
* <div class="js-builds-dropdown-container dropdown-menu"></div> * <div class="js-builds-dropdown-container dropdown-menu"></div>
* </div> * </div>
*/ */
(() => {
class MiniPipelineGraph {
constructor(opts = {}) {
this.container = opts.container || '';
this.dropdownListSelector = '.js-builds-dropdown-container';
this.getBuildsList = this.getBuildsList.bind(this);
this.stopDropdownClickPropagation(); export default class MiniPipelineGraph {
} constructor(opts = {}) {
this.container = opts.container || '';
/** this.dropdownListSelector = '.js-builds-dropdown-container';
* Adds the event listener when the dropdown is opened. this.getBuildsList = this.getBuildsList.bind(this);
* All dropdown events are fired at the .dropdown-menu's parent element. }
*/
bindEvents() {
$(document).off('shown.bs.dropdown', this.container).on('shown.bs.dropdown', this.container, this.getBuildsList);
}
/** /**
* When the user right clicks or cmd/ctrl + click in the job name * Adds the event listener when the dropdown is opened.
* the dropdown should not be closed and the link should open in another tab, * All dropdown events are fired at the .dropdown-menu's parent element.
* so we stop propagation of the click event inside the dropdown. */
* bindEvents() {
* Since this component is rendered multiple times per page we need to guarantee we only $(document).off('shown.bs.dropdown', this.container).on('shown.bs.dropdown', this.container, this.getBuildsList);
* target the click event of this component. }
*/
stopDropdownClickPropagation() {
$(document).on('click', `${this.container} .js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item`, (e) => {
e.stopPropagation();
});
}
/** /**
* For the clicked stage, renders the given data in the dropdown list. * 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,
* @param {HTMLElement} stageContainer * so we stop propagation of the click event inside the dropdown.
* @param {Object} data *
*/ * Since this component is rendered multiple times per page we need to guarantee we only
renderBuildsList(stageContainer, data) { * target the click event of this component.
const dropdownContainer = stageContainer.parentElement.querySelector( */
`${this.dropdownListSelector} .js-builds-dropdown-list`, stopDropdownClickPropagation() {
); document.querySelector(`${this.container} .js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item`).addEventListener('click', (e) => {
e.stopPropagation();
});
}
dropdownContainer.innerHTML = data; /**
} * For the clicked stage, renders the given data in the dropdown list.
*
* @param {HTMLElement} stageContainer
* @param {Object} data
*/
renderBuildsList(stageContainer, data) {
const dropdownContainer = stageContainer.parentElement.querySelector(
`${this.dropdownListSelector} .js-builds-dropdown-list`,
);
/** dropdownContainer.innerHTML = data;
* For the clicked stage, gets the list of builds. }
*
* All dropdown events have a relatedTarget property,
* whose value is the toggling anchor element.
*
* @param {Object} e bootstrap dropdown event
* @return {Promise}
*/
getBuildsList(e) {
const button = e.relatedTarget;
const endpoint = button.dataset.stageEndpoint;
return $.ajax({ /**
dataType: 'json', * For the clicked stage, gets the list of builds.
type: 'GET', *
url: endpoint, * All dropdown events have a relatedTarget property,
beforeSend: () => { * whose value is the toggling anchor element.
this.renderBuildsList(button, ''); *
this.toggleLoading(button); * @param {Object} e bootstrap dropdown event
}, * @return {Promise}
success: (data) => { */
this.toggleLoading(button); getBuildsList(e) {
this.renderBuildsList(button, data.html); const button = e.relatedTarget;
}, const endpoint = button.dataset.stageEndpoint;
error: () => {
this.toggleLoading(button);
new Flash('An error occurred while fetching the builds.', 'alert');
},
});
}
/** return $.ajax({
* Toggles the visibility of the loading icon. dataType: 'json',
* type: 'GET',
* @param {HTMLElement} stageContainer url: endpoint,
* @return {type} beforeSend: () => {
*/ this.renderBuildsList(button, '');
toggleLoading(stageContainer) { this.toggleLoading(button);
stageContainer.parentElement.querySelector( },
`${this.dropdownListSelector} .js-builds-dropdown-loading`, success: (data) => {
).classList.toggle('hidden'); this.toggleLoading(button);
} this.renderBuildsList(button, data.html);
this.stopDropdownClickPropagation();
},
error: () => {
this.toggleLoading(button);
new Flash('An error occurred while fetching the builds.', 'alert');
},
});
} }
window.gl = window.gl || {}; /**
window.gl.MiniPipelineGraph = MiniPipelineGraph; * 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');
}
}
---
title: Prevent builds dropdown to close when the user clicks in a build
merge_request:
author:
/* eslint-disable no-new */ /* eslint-disable no-new */
require('~/flash'); import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
require('~/mini_pipeline_graph_dropdown'); import '~/flash';
(() => { (() => {
describe('Mini Pipeline Graph Dropdown', () => { describe('Mini Pipeline Graph Dropdown', () => {
...@@ -13,7 +13,7 @@ require('~/mini_pipeline_graph_dropdown'); ...@@ -13,7 +13,7 @@ require('~/mini_pipeline_graph_dropdown');
describe('When is initialized', () => { describe('When is initialized', () => {
it('should initialize without errors when no options are given', () => { it('should initialize without errors when no options are given', () => {
const miniPipelineGraph = new window.gl.MiniPipelineGraph(); const miniPipelineGraph = new MiniPipelineGraph();
expect(miniPipelineGraph.dropdownListSelector).toEqual('.js-builds-dropdown-container'); expect(miniPipelineGraph.dropdownListSelector).toEqual('.js-builds-dropdown-container');
}); });
...@@ -21,7 +21,7 @@ require('~/mini_pipeline_graph_dropdown'); ...@@ -21,7 +21,7 @@ require('~/mini_pipeline_graph_dropdown');
it('should set the container as the given prop', () => { it('should set the container as the given prop', () => {
const container = '.foo'; const container = '.foo';
const miniPipelineGraph = new window.gl.MiniPipelineGraph({ container }); const miniPipelineGraph = new MiniPipelineGraph({ container });
expect(miniPipelineGraph.container).toEqual(container); expect(miniPipelineGraph.container).toEqual(container);
}); });
...@@ -29,9 +29,9 @@ require('~/mini_pipeline_graph_dropdown'); ...@@ -29,9 +29,9 @@ require('~/mini_pipeline_graph_dropdown');
describe('When dropdown is clicked', () => { describe('When dropdown is clicked', () => {
it('should call getBuildsList', () => { it('should call getBuildsList', () => {
const getBuildsListSpy = spyOn(gl.MiniPipelineGraph.prototype, 'getBuildsList').and.callFake(function () {}); const getBuildsListSpy = spyOn(MiniPipelineGraph.prototype, 'getBuildsList').and.callFake(function () {});
new gl.MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click(); document.querySelector('.js-builds-dropdown-button').click();
...@@ -41,7 +41,7 @@ require('~/mini_pipeline_graph_dropdown'); ...@@ -41,7 +41,7 @@ require('~/mini_pipeline_graph_dropdown');
it('should make a request to the endpoint provided in the html', () => { it('should make a request to the endpoint provided in the html', () => {
const ajaxSpy = spyOn($, 'ajax').and.callFake(function () {}); const ajaxSpy = spyOn($, 'ajax').and.callFake(function () {});
new gl.MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click(); document.querySelector('.js-builds-dropdown-button').click();
expect(ajaxSpy.calls.allArgs()[0][0].url).toEqual('foobar'); expect(ajaxSpy.calls.allArgs()[0][0].url).toEqual('foobar');
...@@ -53,7 +53,7 @@ require('~/mini_pipeline_graph_dropdown'); ...@@ -53,7 +53,7 @@ require('~/mini_pipeline_graph_dropdown');
html: '\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" href="#"\u003e\u003cspan class="ci-status-icon ci-status-icon-failed"\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003ebuild\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" href="#"\u003e\u003c/a\u003e\n\u003c/li\u003e\n', html: '\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" href="#"\u003e\u003cspan class="ci-status-icon ci-status-icon-failed"\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003ebuild\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" href="#"\u003e\u003c/a\u003e\n\u003c/li\u003e\n',
}); });
}); });
new gl.MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click(); document.querySelector('.js-builds-dropdown-button').click();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment