16fb2bb919
Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
649 lines
16 KiB
JavaScript
649 lines
16 KiB
JavaScript
import Tooltip from '../../src/index.js';
|
|
import '@popperjs/test-utils';
|
|
import then from '@popperjs/test-utils/utils/then.js';
|
|
import '@popperjs/test-utils/utils/customEventPolyfill.js';
|
|
|
|
const jasmineWrapper = document.getElementById('jasmineWrapper');
|
|
const isIE10 = navigator.appVersion.indexOf('MSIE 10') !== -1;
|
|
|
|
let reference;
|
|
let instance;
|
|
function createReference() {
|
|
reference = document.createElement('div');
|
|
reference.style.width = '100px';
|
|
reference.style.height = '100px';
|
|
reference.style.margin = '100px';
|
|
reference.textContent = 'reference';
|
|
jasmineWrapper.appendChild(reference);
|
|
}
|
|
|
|
describe('[tooltip.js]', () => {
|
|
describe('manual', () => {
|
|
beforeEach(() => {
|
|
createReference();
|
|
});
|
|
|
|
it('should show tooltip', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).not.toBeNull();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should hide tooltip', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
});
|
|
|
|
instance.show();
|
|
then(() => instance.hide());
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should toggle (show) tooltip', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
});
|
|
|
|
instance.toggle();
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).not.toBeNull();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should toggle (hide) tooltip', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
});
|
|
|
|
instance.show();
|
|
then(() => instance.toggle());
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should show, hide and show again a tooltip', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
});
|
|
|
|
instance.show();
|
|
then(() => instance.hide());
|
|
then(() => instance.show());
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).not.toBeNull();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should dispose tooltip', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
});
|
|
|
|
instance.show();
|
|
then(() => instance.dispose());
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
});
|
|
|
|
then(() => {
|
|
reference.click();
|
|
});
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should dispose tooltip despite never being shown', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
});
|
|
|
|
instance.dispose();
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
});
|
|
|
|
then(() => {
|
|
reference.dispatchEvent(new CustomEvent('mouseenter'));
|
|
});
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('container', () => {
|
|
beforeEach(() => {
|
|
createReference();
|
|
});
|
|
|
|
it('should show tooltip as child of body', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
container: 'body',
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip').parentNode).toBe(
|
|
document.body
|
|
);
|
|
instance.dispose();
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('content', () => {
|
|
beforeEach(() => {
|
|
createReference();
|
|
});
|
|
|
|
it('should show text tooltip', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').textContent
|
|
).toBe('foobar');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should show HTML tooltip', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: '<strong>foobar</strong>',
|
|
html: true,
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').innerHTML
|
|
).toBe('<strong>foobar</strong>');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should show stripped out HTML tooltip', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: '<strong>foobar</strong>',
|
|
html: true,
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').textContent
|
|
).toBe('foobar');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should use a DOM node as tooltip content', done => {
|
|
const content = document.createElement('div');
|
|
content.textContent = 'foobar';
|
|
instance = new Tooltip(reference, {
|
|
title: content,
|
|
html: true,
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').innerHTML
|
|
).toBe('<div>foobar</div>');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should use a document fragment as tooltip content', done => {
|
|
const content = document.createDocumentFragment();
|
|
const inner = document.createElement('div');
|
|
inner.textContent = 'test';
|
|
content.appendChild(inner);
|
|
instance = new Tooltip(reference, {
|
|
title: content,
|
|
html: true,
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').innerHTML
|
|
).toBe('<div>test</div>');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should use a string returned by a function as tooltip content', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: () => 'foobar',
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').textContent
|
|
).toBe('foobar');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should use a DOM node returned by a function as tooltip content', done => {
|
|
const content = document.createElement('div');
|
|
content.textContent = 'foobar';
|
|
instance = new Tooltip(reference, {
|
|
title: () => content,
|
|
html: true,
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').innerHTML
|
|
).toBe('<div>foobar</div>');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should use a document fragment returned by a function as tooltip content', done => {
|
|
const content = document.createDocumentFragment();
|
|
const inner = document.createElement('div');
|
|
inner.textContent = 'test';
|
|
content.appendChild(inner);
|
|
instance = new Tooltip(reference, {
|
|
title: () => content,
|
|
html: true,
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').innerHTML
|
|
).toBe('<div>test</div>');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should not use dom node returned by function as tooltip content if html option disabled', done => {
|
|
const content = document.createElement('div');
|
|
content.textContent = 'test';
|
|
instance = new Tooltip(reference, {
|
|
title: () => content,
|
|
html: false,
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').innerHTML
|
|
).toBe('');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should use a custom template with empty leading&trailing spaces', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
template: `
|
|
<div class="tooltip" role="tooltip">
|
|
<div class="tooltip-arrow"></div>
|
|
<div class="foobar"><div class="tooltip-inner"></div></div>
|
|
</div>
|
|
`,
|
|
});
|
|
|
|
instance.show();
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip .foobar').textContent).toBe(
|
|
'foobar'
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should update title content with String', done => {
|
|
const updatedContent = 'Updated string';
|
|
instance = new Tooltip(reference, {
|
|
title: 'Constructor message',
|
|
});
|
|
|
|
instance.show();
|
|
|
|
instance.updateTitleContent(updatedContent);
|
|
|
|
then(() => {
|
|
expect(
|
|
document.querySelector('.tooltip .tooltip-inner').textContent
|
|
).toBe(updatedContent);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should update title content with HTMLElement', done => {
|
|
const updatedContent = 'Updated with div element';
|
|
const el = document.createElement('div');
|
|
el.textContent = updatedContent;
|
|
instance = new Tooltip(reference, {
|
|
title: 'Constructor message',
|
|
html: true,
|
|
});
|
|
|
|
instance.show();
|
|
|
|
instance.updateTitleContent(el);
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip-inner').innerHTML).toBe(
|
|
el.outerHTML
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should update the tooltip position when changing the title', done => {
|
|
// Unreliable on IE...
|
|
if (isIE10) {
|
|
pending();
|
|
}
|
|
const updatedContent = 'Updated string with a different length';
|
|
instance = new Tooltip(reference, {
|
|
title: 'Constructor message',
|
|
});
|
|
|
|
instance.show();
|
|
|
|
const oldPosition = document
|
|
.querySelector('.tooltip .tooltip-inner')
|
|
.getBoundingClientRect().left;
|
|
|
|
instance.updateTitleContent(updatedContent);
|
|
|
|
then(() => {
|
|
expect(
|
|
document
|
|
.querySelector('.tooltip .tooltip-inner')
|
|
.getBoundingClientRect().left
|
|
).not.toBe(oldPosition);
|
|
done();
|
|
}, 500);
|
|
});
|
|
});
|
|
|
|
describe('events', () => {
|
|
beforeEach(() => {
|
|
createReference();
|
|
});
|
|
|
|
afterEach(() => {
|
|
instance.dispose();
|
|
jasmineWrapper.innerHTML = '';
|
|
});
|
|
|
|
it('should show a tooltip when hovered', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
trigger: 'hover',
|
|
});
|
|
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
|
|
reference.dispatchEvent(new CustomEvent('mouseenter'));
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).not.toBeNull();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should hide a tooltip on reference mouseleave', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
trigger: 'hover',
|
|
});
|
|
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
|
|
reference.dispatchEvent(new CustomEvent('mouseenter'));
|
|
then(() => reference.dispatchEvent(new CustomEvent('mouseleave')), 200);
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it('should hide a tooltip on tooltip mouseleave', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
trigger: 'hover',
|
|
});
|
|
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
|
|
reference.dispatchEvent(new CustomEvent('mouseenter'));
|
|
then(() => reference.dispatchEvent(new CustomEvent('mouseleave')));
|
|
then(() =>
|
|
document
|
|
.querySelector('.tooltip')
|
|
.dispatchEvent(new CustomEvent('mouseenter'))
|
|
);
|
|
then(() =>
|
|
document
|
|
.querySelector('.tooltip')
|
|
.dispatchEvent(new CustomEvent('mouseleave'))
|
|
);
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it('should not hide a tooltip if user mouseenter tooltip and then mouseenter reference element again', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
trigger: 'hover',
|
|
});
|
|
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
|
|
reference.dispatchEvent(new CustomEvent('mouseenter'));
|
|
then(() => reference.dispatchEvent(new CustomEvent('mouseleave')));
|
|
then(() =>
|
|
document
|
|
.querySelector('.tooltip')
|
|
.dispatchEvent(new CustomEvent('mouseenter'))
|
|
);
|
|
then(() =>
|
|
document
|
|
.querySelector('.tooltip')
|
|
.dispatchEvent(new CustomEvent('mouseleave'))
|
|
);
|
|
then(() => reference.dispatchEvent(new CustomEvent('mouseenter')));
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip').style.visibility).toBe('visible');
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it('should show a tooltip on click', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
trigger: 'click',
|
|
});
|
|
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
|
|
reference.dispatchEvent(new CustomEvent('click'));
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).not.toBeNull();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should not show tooltip if mouse leaves reference before tooltip is shown', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
trigger: 'hover',
|
|
delay: { show: 1000, hide: 0 },
|
|
});
|
|
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
|
|
reference.dispatchEvent(new CustomEvent('mouseenter'));
|
|
reference.dispatchEvent(new CustomEvent('mouseleave'));
|
|
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should not flash the tooltip if mouse leaves reference before tooltip is shown', done => {
|
|
const delay = 500;
|
|
const hoverTime = 200;
|
|
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
trigger: 'hover',
|
|
delay,
|
|
});
|
|
|
|
spyOn(instance, '_show');
|
|
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
|
|
then(() => reference.dispatchEvent(new CustomEvent('mouseenter')), hoverTime);
|
|
then(() => reference.dispatchEvent(new CustomEvent('mouseleave')), delay * 2);
|
|
|
|
then(() => {
|
|
expect(instance._show).not.toHaveBeenCalled();
|
|
done()
|
|
});
|
|
});
|
|
|
|
it('should hide a tooltip on click while open', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
trigger: 'click',
|
|
});
|
|
|
|
expect(document.querySelector('.tooltip')).toBeNull();
|
|
|
|
reference.dispatchEvent(new CustomEvent('click'));
|
|
then(() => reference.dispatchEvent(new CustomEvent('click')));
|
|
then(() => {
|
|
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('options', () => {
|
|
beforeEach(() => {
|
|
createReference();
|
|
});
|
|
|
|
it('should proxy the `options.offset` value to the Popper.js instance', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'test',
|
|
offset: 10,
|
|
}).show();
|
|
expect(instance._popperOptions.modifiers.offset.offset).toBe(10);
|
|
done();
|
|
});
|
|
|
|
it('should proxy a `offset` modifier to the Popper.js instance', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'test',
|
|
popperOptions: {
|
|
modifiers: { offset: { offset: 10 } },
|
|
},
|
|
}).show();
|
|
expect(instance._popperOptions.modifiers.offset.offset).toBe(10);
|
|
done();
|
|
});
|
|
|
|
it('should hide on click outside with `options.closeOnClickOutside`', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'foobar',
|
|
trigger: 'click',
|
|
closeOnClickOutside: true,
|
|
});
|
|
reference.dispatchEvent(new CustomEvent('click'));
|
|
then(() => expect(document.querySelector('.tooltip')).not.toBeNull());
|
|
then(() => document.body.dispatchEvent(new CustomEvent('mousedown')));
|
|
then(() => expect(document.querySelector('.tooltip').style.visibility).toBe('hidden'));
|
|
then(done);
|
|
});
|
|
|
|
it('should proxy the arrow modifier to the Popper.js instance', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'test',
|
|
popperOptions: {
|
|
modifiers: {
|
|
arrow: {
|
|
enabled: false,
|
|
},
|
|
},
|
|
},
|
|
}).show();
|
|
expect(instance._popperOptions.modifiers.arrow.enabled).toBe(false);
|
|
done();
|
|
});
|
|
|
|
it('should proxy the offset modifier to the Popper.js instance', done => {
|
|
instance = new Tooltip(reference, {
|
|
title: 'test',
|
|
popperOptions: {
|
|
modifiers: {
|
|
offset: {
|
|
enabled: false,
|
|
},
|
|
},
|
|
},
|
|
}).show();
|
|
expect(instance._popperOptions.modifiers.offset.enabled).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
});
|