Commit 6698021a authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '229044-bootstrap-tooltip-api-facade' into 'master'

Bootstrap tooltip API facade

Closes #229044

See merge request gitlab-org/gitlab!42399
parents 5e3c8ce1 a6e28b5c
import Vue from 'vue';
import { toArray } from 'lodash';
import jQuery from 'jquery';
import { toArray, isFunction } from 'lodash';
import Tooltips from './components/tooltips.vue';
let app;
......@@ -53,26 +54,66 @@ const handleTooltipEvent = (rootTarget, e, selector, config = {}) => {
}
};
export const initTooltips = (selector, config = {}) => {
const triggers = config?.triggers || DEFAULT_TRIGGER;
const events = triggers.split(' ').map(trigger => EVENTS_MAP[trigger]);
const applyToElements = (elements, handler) => toArray(elements).forEach(handler);
events.forEach(event => {
document.addEventListener(event, e => handleTooltipEvent(document, e, selector, config), true);
});
const invokeBootstrapApi = (elements, method) => {
if (isFunction(elements.tooltip)) {
jQuery(elements).tooltip(method);
}
};
return tooltipsApp();
const isGlTooltipsEnabled = () => Boolean(window.gon.glTooltipsEnabled);
const tooltipApiInvoker = ({ glHandler, bsHandler }) => (elements, ...params) => {
if (isGlTooltipsEnabled()) {
applyToElements(elements, glHandler);
} else {
bsHandler(elements, ...params);
}
};
const elementsIterator = handler => elements => toArray(elements).forEach(handler);
export const initTooltips = (config = {}) => {
if (isGlTooltipsEnabled()) {
const triggers = config?.triggers || DEFAULT_TRIGGER;
const events = triggers.split(' ').map(trigger => EVENTS_MAP[trigger]);
export const dispose = elementsIterator(element => tooltipsApp().dispose(element));
export const fixTitle = elementsIterator(element => tooltipsApp().fixTitle(element));
export const enable = elementsIterator(element => tooltipsApp().triggerEvent(element, 'enable'));
export const disable = elementsIterator(element => tooltipsApp().triggerEvent(element, 'disable'));
export const hide = elementsIterator(element => tooltipsApp().triggerEvent(element, 'close'));
export const show = elementsIterator(element => tooltipsApp().triggerEvent(element, 'open'));
events.forEach(event => {
document.addEventListener(
event,
e => handleTooltipEvent(document, e, config.selector, config),
true,
);
});
return tooltipsApp();
}
return invokeBootstrapApi(document.body, config);
};
export const dispose = tooltipApiInvoker({
glHandler: element => tooltipsApp().dispose(element),
bsHandler: elements => invokeBootstrapApi(elements, 'dispose'),
});
export const fixTitle = tooltipApiInvoker({
glHandler: element => tooltipsApp().fixTitle(element),
bsHandler: elements => invokeBootstrapApi(elements, '_fixTitle'),
});
export const enable = tooltipApiInvoker({
glHandler: element => tooltipsApp().triggerEvent(element, 'enable'),
bsHandler: elements => invokeBootstrapApi(elements, 'enable'),
});
export const disable = tooltipApiInvoker({
glHandler: element => tooltipsApp().triggerEvent(element, 'disable'),
bsHandler: elements => invokeBootstrapApi(elements, 'disable'),
});
export const hide = tooltipApiInvoker({
glHandler: element => tooltipsApp().triggerEvent(element, 'close'),
bsHandler: elements => invokeBootstrapApi(elements, 'hide'),
});
export const show = tooltipApiInvoker({
glHandler: element => tooltipsApp().triggerEvent(element, 'open'),
bsHandler: elements => invokeBootstrapApi(elements, 'show'),
});
export const destroy = () => {
tooltipsApp().$destroy();
app = null;
......
import jQuery from 'jquery';
import { initTooltips, dispose, destroy, hide, show, enable, disable, fixTitle } from '~/tooltips';
describe('tooltips/index.js', () => {
......@@ -21,7 +22,7 @@ describe('tooltips/index.js', () => {
};
const buildTooltipsApp = () => {
tooltipsApp = initTooltips('.has-tooltip');
tooltipsApp = initTooltips({ selector: '.has-tooltip' });
};
const triggerEvent = (target, eventName = 'mouseenter') => {
......@@ -30,6 +31,10 @@ describe('tooltips/index.js', () => {
target.dispatchEvent(event);
};
beforeEach(() => {
window.gon.glTooltipsEnabled = true;
});
afterEach(() => {
document.body.childNodes.forEach(node => node.remove());
destroy();
......@@ -117,4 +122,28 @@ describe('tooltips/index.js', () => {
expect(tooltipsApp.fixTitle).toHaveBeenCalledWith(target);
});
describe('when glTooltipsEnabled feature flag is disabled', () => {
beforeEach(() => {
window.gon.glTooltipsEnabled = false;
});
it.each`
method | methodName | bootstrapParams
${dispose} | ${'dispose'} | ${'dispose'}
${fixTitle} | ${'fixTitle'} | ${'_fixTitle'}
${enable} | ${'enable'} | ${'enable'}
${disable} | ${'disable'} | ${'disable'}
${hide} | ${'hide'} | ${'hide'}
${show} | ${'show'} | ${'show'}
`('delegates $methodName to bootstrap tooltip API', ({ method, bootstrapParams }) => {
const elements = jQuery(createTooltipTarget());
jest.spyOn(jQuery.fn, 'tooltip');
method(elements);
expect(elements.tooltip).toHaveBeenCalledWith(bootstrapParams);
});
});
});
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