300 lines
9.4 KiB
JavaScript
300 lines
9.4 KiB
JavaScript
|
import Popper from '../../src/index.js';
|
||
|
import makePopperFactory from '../utils/makePopperFactory';
|
||
|
import makeConnectedElement from '@popperjs/test-utils/utils/makeConnectedElement';
|
||
|
import makeConnectedScrollElement from '@popperjs/test-utils/utils/makeConnectedScrollElement';
|
||
|
import makeElement from '@popperjs/test-utils/utils/makeElement';
|
||
|
import '@popperjs/test-utils/setup';
|
||
|
|
||
|
[true, false].forEach((positionFixed) => {
|
||
|
|
||
|
describe('[lifecycle]' + (positionFixed ? ' Fixed' : ''), () => {
|
||
|
|
||
|
beforeEach(function(){
|
||
|
Popper.Defaults.positionFixed = positionFixed;
|
||
|
});
|
||
|
|
||
|
const makePopper = makePopperFactory();
|
||
|
|
||
|
describe('on creation', () => {
|
||
|
it('adds a resize event listener', () => {
|
||
|
spyOn(window, 'addEventListener');
|
||
|
|
||
|
const { state } = makePopper(
|
||
|
makeConnectedElement(),
|
||
|
makeConnectedElement()
|
||
|
);
|
||
|
|
||
|
expect(window.addEventListener.calls.argsFor(0)).toEqual([
|
||
|
'resize',
|
||
|
state.updateBound,
|
||
|
{ passive: true },
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
it('adds a scroll event listener to window when boundariesElement is viewport', () => {
|
||
|
spyOn(window, 'addEventListener');
|
||
|
|
||
|
const {
|
||
|
state,
|
||
|
} = makePopper(makeConnectedElement(), makeConnectedElement(), {
|
||
|
boundariesElement: 'viewport',
|
||
|
});
|
||
|
|
||
|
expect(window.addEventListener.calls.argsFor(1)).toEqual([
|
||
|
'scroll',
|
||
|
state.updateBound,
|
||
|
{ passive: true },
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
it('adds a scroll event listener to the closest scroll ancestor of reference when boundariesElement is an element', () => {
|
||
|
const boundariesElement = makeConnectedElement();
|
||
|
const scrollAncestor = document.createElement('div');
|
||
|
const reference = document.createElement('div');
|
||
|
const popper = document.createElement('div');
|
||
|
|
||
|
boundariesElement.appendChild(scrollAncestor);
|
||
|
scrollAncestor.appendChild(reference);
|
||
|
scrollAncestor.appendChild(popper);
|
||
|
|
||
|
scrollAncestor.style.overflow = 'scroll';
|
||
|
|
||
|
spyOn(scrollAncestor, 'addEventListener');
|
||
|
const { state } = makePopper(reference, popper, {
|
||
|
boundariesElement: boundariesElement,
|
||
|
});
|
||
|
|
||
|
expect(scrollAncestor.addEventListener.calls.allArgs()).toEqual([
|
||
|
['scroll', state.updateBound, { passive: true }],
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
it('should not add resize/scroll event if eventsEnabled option is set to false', () => {
|
||
|
const {
|
||
|
state,
|
||
|
} = makePopper(makeConnectedElement(), makeConnectedElement(), {
|
||
|
eventsEnabled: false,
|
||
|
});
|
||
|
|
||
|
expect(state.eventsEnabled).toBe(false);
|
||
|
expect(state.updateBound).toBeUndefined();
|
||
|
expect(state.scrollElement).toBeUndefined();
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('on update', () => {
|
||
|
it('should call update callback', done => {
|
||
|
const popperElement = makeConnectedElement();
|
||
|
|
||
|
let isOnCreateCalled = false;
|
||
|
|
||
|
new Popper(makeConnectedElement(), popperElement, {
|
||
|
onCreate: data => {
|
||
|
isOnCreateCalled = true;
|
||
|
data.instance.scheduleUpdate();
|
||
|
},
|
||
|
onUpdate: () => {
|
||
|
// makes sure it's executed after `onCreate`
|
||
|
expect(isOnCreateCalled).toBe(true);
|
||
|
// makes sure it's executed
|
||
|
done();
|
||
|
},
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('on destroy', () => {
|
||
|
|
||
|
const mockStyles = {
|
||
|
top: '100px',
|
||
|
left: '100px',
|
||
|
right: '100px',
|
||
|
bottom: '100px',
|
||
|
position: 'absolute',
|
||
|
transform: 'translate3d(100px, 100px, 0)',
|
||
|
willChange: 'transform',
|
||
|
}
|
||
|
|
||
|
it('removes the resize event listener from window', () => {
|
||
|
spyOn(window, 'removeEventListener');
|
||
|
|
||
|
const instance = new Popper(
|
||
|
makeConnectedElement(),
|
||
|
makeConnectedElement()
|
||
|
);
|
||
|
const { updateBound } = instance.state;
|
||
|
instance.destroy();
|
||
|
|
||
|
expect(window.removeEventListener.calls.argsFor(0)).toEqual([
|
||
|
'resize',
|
||
|
updateBound,
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
it('removes the scroll event listener from window', () => {
|
||
|
spyOn(window, 'removeEventListener');
|
||
|
|
||
|
const instance = new Popper(
|
||
|
makeConnectedElement(),
|
||
|
makeConnectedElement()
|
||
|
);
|
||
|
const { updateBound } = instance.state;
|
||
|
instance.destroy();
|
||
|
|
||
|
expect(window.removeEventListener.calls.argsFor(1)).toEqual([
|
||
|
'scroll',
|
||
|
updateBound,
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
it('should clean up the popper element\'s styles if modifiers.applyStyle is enabled', () => {
|
||
|
const popperElement = makeConnectedElement();
|
||
|
|
||
|
for (const key in mockStyles) {
|
||
|
if (mockStyles.hasOwnProperty(key)) {
|
||
|
popperElement.style[key] = mockStyles[key];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const instance = new Popper(makeConnectedElement(), popperElement, {
|
||
|
modifiers: { applyStyle: { enabled: true } },
|
||
|
});
|
||
|
|
||
|
instance.destroy();
|
||
|
|
||
|
for (const key in mockStyles) {
|
||
|
if (mockStyles.hasOwnProperty(key)) {
|
||
|
expect(popperElement.style[key]).toBe('');
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
it('should not modify the popper element\'s styles if modifiers.applyStyle is disabled', () => {
|
||
|
const popperElement = makeConnectedElement();
|
||
|
|
||
|
for (const key in mockStyles) {
|
||
|
if (mockStyles.hasOwnProperty(key)) {
|
||
|
popperElement.style[key] = mockStyles[key];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const instance = new Popper(makeConnectedElement(), popperElement, {
|
||
|
modifiers: { applyStyle: { enabled: false } },
|
||
|
});
|
||
|
|
||
|
instance.destroy();
|
||
|
|
||
|
for (const key in mockStyles) {
|
||
|
if (mockStyles.hasOwnProperty(key)) {
|
||
|
expect(popperElement.style[key]).not.toBe('');
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
it('should not call update after destroy', () => {
|
||
|
let isUpdateCalled = false;
|
||
|
|
||
|
const popperElement = makeConnectedElement();
|
||
|
|
||
|
const instance = new Popper(makeConnectedElement(), popperElement, {
|
||
|
onUpdate: () => {
|
||
|
isUpdateCalled = true;
|
||
|
},
|
||
|
});
|
||
|
instance.update();
|
||
|
instance.destroy();
|
||
|
expect(isUpdateCalled).toBe(false);
|
||
|
});
|
||
|
|
||
|
describe('when boundariesElement is not `window` and the scroll parent is not `window`', () => {
|
||
|
it('removes the scroll event listener from the scroll parent', () => {
|
||
|
const boundariesElement = makeConnectedScrollElement();
|
||
|
const reference = boundariesElement.appendChild(makeElement());
|
||
|
const popper = boundariesElement.appendChild(makeElement());
|
||
|
|
||
|
spyOn(boundariesElement, 'removeEventListener');
|
||
|
|
||
|
const instance = new Popper(reference, popper, {
|
||
|
boundariesElement: boundariesElement,
|
||
|
});
|
||
|
const { updateBound } = instance.state;
|
||
|
instance.destroy();
|
||
|
|
||
|
expect(boundariesElement.removeEventListener.calls.allArgs()).toEqual([
|
||
|
['scroll', updateBound],
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
describe('when the reference is disconnected from the DOM', () => {
|
||
|
it('removes the scroll event listener from the scroll parent when the reference is disconnected from the DOM', () => {
|
||
|
const boundariesElement = makeConnectedScrollElement();
|
||
|
const reference = boundariesElement.appendChild(makeElement());
|
||
|
const popper = boundariesElement.appendChild(makeElement());
|
||
|
|
||
|
spyOn(boundariesElement, 'removeEventListener');
|
||
|
|
||
|
const instance = new Popper(reference, popper, {
|
||
|
boundariesElement: boundariesElement,
|
||
|
});
|
||
|
const { updateBound } = instance.state;
|
||
|
|
||
|
boundariesElement.removeChild(reference);
|
||
|
instance.destroy();
|
||
|
|
||
|
expect(
|
||
|
boundariesElement.removeEventListener.calls.allArgs()
|
||
|
).toEqual([['scroll', updateBound]]);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('methods: enableEventListeners/disableEventListeners', () => {
|
||
|
it('enableEventListeners', () => {
|
||
|
spyOn(window, 'addEventListener');
|
||
|
const instance = makePopper(
|
||
|
makeConnectedElement(),
|
||
|
makeConnectedElement(),
|
||
|
{ eventsEnabled: false }
|
||
|
);
|
||
|
|
||
|
instance.enableEventListeners();
|
||
|
expect(window.addEventListener.calls.count()).toEqual(2);
|
||
|
expect(window.addEventListener.calls.argsFor(0)).toEqual([
|
||
|
'resize',
|
||
|
instance.state.updateBound,
|
||
|
{ passive: true },
|
||
|
]);
|
||
|
expect(window.addEventListener.calls.argsFor(1)).toEqual([
|
||
|
'scroll',
|
||
|
instance.state.updateBound,
|
||
|
{ passive: true },
|
||
|
]);
|
||
|
expect(instance.state.eventsEnabled).toBe(true);
|
||
|
});
|
||
|
|
||
|
it('disableEventListeners', () => {
|
||
|
spyOn(window, 'removeEventListener');
|
||
|
const instance = makePopper(
|
||
|
makeConnectedElement(),
|
||
|
makeConnectedElement()
|
||
|
);
|
||
|
const { updateBound } = instance.state;
|
||
|
|
||
|
instance.disableEventListeners();
|
||
|
expect(window.removeEventListener.calls.argsFor(0)).toEqual([
|
||
|
'resize',
|
||
|
updateBound,
|
||
|
]);
|
||
|
expect(window.removeEventListener.calls.argsFor(1)).toEqual([
|
||
|
'scroll',
|
||
|
updateBound,
|
||
|
]);
|
||
|
expect(instance.state.eventsEnabled).toBe(false);
|
||
|
expect(instance.state.updateBound).toBe(null);
|
||
|
expect(instance.state.scrollElement).toBe(null);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|