mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-22 02:50:36 +03:00
improve tests and code
This commit is contained in:
@@ -37,7 +37,7 @@ export interface OverlayScrollbarsStatic {
|
|||||||
): OverlayScrollbars;
|
): OverlayScrollbars;
|
||||||
|
|
||||||
plugin(plugin: Plugin | Plugin[]): void;
|
plugin(plugin: Plugin | Plugin[]): void;
|
||||||
valid(osInstance: any): boolean;
|
valid(osInstance: any): osInstance is OverlayScrollbars;
|
||||||
env(): Environment;
|
env(): Environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,19 +94,19 @@ export interface OverlayScrollbars {
|
|||||||
options(): Options;
|
options(): Options;
|
||||||
options(newOptions: DeepPartial<Options>): Options;
|
options(newOptions: DeepPartial<Options>): Options;
|
||||||
|
|
||||||
update(force?: boolean): OverlayScrollbars;
|
|
||||||
|
|
||||||
destroy(): void;
|
|
||||||
|
|
||||||
state(): State;
|
|
||||||
|
|
||||||
elements(): Elements;
|
|
||||||
|
|
||||||
on<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>): () => void;
|
on<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>): () => void;
|
||||||
on<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>[]): () => void;
|
on<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>[]): () => void;
|
||||||
|
|
||||||
off<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>): void;
|
off<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>): void;
|
||||||
off<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>[]): void;
|
off<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>[]): void;
|
||||||
|
|
||||||
|
update(force?: boolean): boolean;
|
||||||
|
|
||||||
|
state(): State;
|
||||||
|
|
||||||
|
elements(): Elements;
|
||||||
|
|
||||||
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
||||||
@@ -115,11 +115,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
options?: DeepPartial<Options>,
|
options?: DeepPartial<Options>,
|
||||||
eventListeners?: InitialEventListeners
|
eventListeners?: InitialEventListeners
|
||||||
) => {
|
) => {
|
||||||
const {
|
const { _getDefaultOptions, _getDefaultInitialization, _addListener } = getEnvironment();
|
||||||
_getDefaultOptions,
|
|
||||||
_getDefaultInitialization,
|
|
||||||
_addListener: addEnvListener,
|
|
||||||
} = getEnvironment();
|
|
||||||
const plugins = getPlugins();
|
const plugins = getPlugins();
|
||||||
const targetIsElement = isHTMLElement(target);
|
const targetIsElement = isHTMLElement(target);
|
||||||
const instanceTarget = targetIsElement ? target : target.target;
|
const instanceTarget = targetIsElement ? target : target.target;
|
||||||
@@ -149,10 +145,9 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
currentOptions,
|
currentOptions,
|
||||||
structureState
|
structureState
|
||||||
);
|
);
|
||||||
const update = (changedOptions: DeepPartial<Options>, force?: boolean) => {
|
const update = (changedOptions: DeepPartial<Options>, force?: boolean): boolean =>
|
||||||
updateStructure(changedOptions, !!force);
|
updateStructure(changedOptions, !!force);
|
||||||
};
|
const removeEnvListener = _addListener(update.bind(0, {}, true));
|
||||||
const removeEnvListener = addEnvListener(update.bind(0, {}, true));
|
|
||||||
const destroy = (canceled?: boolean) => {
|
const destroy = (canceled?: boolean) => {
|
||||||
removeInstance(instanceTarget);
|
removeInstance(instanceTarget);
|
||||||
removeEnvListener();
|
removeEnvListener();
|
||||||
@@ -257,10 +252,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
update(force?: boolean) {
|
update: (force?: boolean) => update({}, force),
|
||||||
update({}, force);
|
|
||||||
return instance;
|
|
||||||
},
|
|
||||||
destroy: destroy.bind(0),
|
destroy: destroy.bind(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,13 +315,15 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return instance.update(true);
|
instance.update(true);
|
||||||
|
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
return potentialInstance!;
|
return potentialInstance!;
|
||||||
};
|
};
|
||||||
|
|
||||||
OverlayScrollbars.plugin = addPlugin;
|
OverlayScrollbars.plugin = addPlugin;
|
||||||
OverlayScrollbars.valid = (osInstance: any) => {
|
OverlayScrollbars.valid = (osInstance: any): osInstance is OverlayScrollbars => {
|
||||||
const hasElmsFn = osInstance && (osInstance as OverlayScrollbars).elements;
|
const hasElmsFn = osInstance && (osInstance as OverlayScrollbars).elements;
|
||||||
const elements = isFunction(hasElmsFn) && hasElmsFn();
|
const elements = isFunction(hasElmsFn) && hasElmsFn();
|
||||||
return isPlainObject(elements) && !!getInstance(elements.target);
|
return isPlainObject(elements) && !!getInstance(elements.target);
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import type { DeepPartial } from 'typings';
|
|||||||
|
|
||||||
export type SetupElements<T extends Record<string, any>> = [elements: T, destroy: () => void];
|
export type SetupElements<T extends Record<string, any>> = [elements: T, destroy: () => void];
|
||||||
|
|
||||||
export type SetupUpdate<T extends any[]> = (
|
export type SetupUpdate<Args extends any[], R> = (
|
||||||
changedOptions: DeepPartial<Options>,
|
changedOptions: DeepPartial<Options>,
|
||||||
force: boolean,
|
force: boolean,
|
||||||
...args: T
|
...args: Args
|
||||||
) => void;
|
) => R;
|
||||||
|
|
||||||
export type SetupUpdateCheckOption = <T>(path: string) => [value: T, changed: boolean];
|
export type SetupUpdateCheckOption = <T>(path: string) => [value: T, changed: boolean];
|
||||||
|
|
||||||
@@ -26,8 +26,9 @@ export type SetupState<T extends Record<string, any>> = [
|
|||||||
export type Setup<
|
export type Setup<
|
||||||
DynamicState,
|
DynamicState,
|
||||||
StaticState extends Record<string, any> = Record<string, any>,
|
StaticState extends Record<string, any> = Record<string, any>,
|
||||||
A extends any[] = []
|
Args extends any[] = [],
|
||||||
> = [update: SetupUpdate<A>, state: (() => DynamicState) & StaticState, destroy: () => void];
|
R = void
|
||||||
|
> = [update: SetupUpdate<Args, R>, state: (() => DynamicState) & StaticState, destroy: () => void];
|
||||||
|
|
||||||
const getPropByPath = <T>(obj: any, path: string): T =>
|
const getPropByPath = <T>(obj: any, path: string): T =>
|
||||||
obj
|
obj
|
||||||
|
|||||||
@@ -68,34 +68,33 @@ const initialStructureSetupUpdateState: StructureSetupState = {
|
|||||||
export const createStructureSetup = (
|
export const createStructureSetup = (
|
||||||
target: InitializationTarget,
|
target: InitializationTarget,
|
||||||
options: ReadonlyOptions
|
options: ReadonlyOptions
|
||||||
): Setup<StructureSetupState, StructureSetupStaticState> => {
|
): Setup<StructureSetupState, StructureSetupStaticState, [], boolean> => {
|
||||||
const checkOptionsFallback = createOptionCheck(options, {});
|
const checkOptionsFallback = createOptionCheck(options, {});
|
||||||
const state = createState(initialStructureSetupUpdateState);
|
const state = createState(initialStructureSetupUpdateState);
|
||||||
const [addEvent, removeEvent, triggerEvent] = createEventListenerHub<StructureSetupEventMap>();
|
const [addEvent, removeEvent, triggerEvent] = createEventListenerHub<StructureSetupEventMap>();
|
||||||
const [getState] = state;
|
const [getState] = state;
|
||||||
const [elements, appendStructureElements, destroyElements] = createStructureSetupElements(target);
|
const [elements, appendStructureElements, destroyElements] = createStructureSetupElements(target);
|
||||||
const updateStructure = createStructureSetupUpdate(elements, state);
|
const updateStructure = createStructureSetupUpdate(elements, state);
|
||||||
const triggerUpdateEvent: (...args: StructureSetupEventMap['u']) => void = (
|
const triggerUpdateEvent: (...args: StructureSetupEventMap['u']) => boolean = (
|
||||||
updateHints,
|
updateHints,
|
||||||
changedOptions,
|
changedOptions,
|
||||||
force
|
force
|
||||||
) => {
|
) => {
|
||||||
const truthyUpdateHints = keys(updateHints).some((key) => updateHints[key]);
|
const truthyUpdateHints = keys(updateHints).some((key) => updateHints[key]);
|
||||||
|
const changed = truthyUpdateHints || !isEmptyObject(changedOptions) || force;
|
||||||
if (truthyUpdateHints || !isEmptyObject(changedOptions) || force) {
|
if (changed) {
|
||||||
triggerEvent('u', [updateHints, changedOptions, force]);
|
triggerEvent('u', [updateHints, changedOptions, force]);
|
||||||
}
|
}
|
||||||
|
return changed;
|
||||||
};
|
};
|
||||||
const [destroyObservers, appendObserverElements, updateObservers, updateObserversOptions] =
|
const [destroyObservers, appendObserverElements, updateObservers, updateObserversOptions] =
|
||||||
createStructureSetupObservers(elements, state, (updateHints) => {
|
createStructureSetupObservers(elements, state, (updateHints) =>
|
||||||
triggerUpdateEvent(updateStructure(checkOptionsFallback, updateHints), {}, false);
|
triggerUpdateEvent(updateStructure(checkOptionsFallback, updateHints), {}, false)
|
||||||
});
|
);
|
||||||
|
|
||||||
const structureSetupState = getState.bind(0) as (() => StructureSetupState) &
|
const structureSetupState = getState.bind(0) as (() => StructureSetupState) &
|
||||||
StructureSetupStaticState;
|
StructureSetupStaticState;
|
||||||
structureSetupState._addOnUpdatedListener = (listener) => {
|
structureSetupState._addOnUpdatedListener = (listener) => addEvent('u', listener);
|
||||||
addEvent('u', listener);
|
|
||||||
};
|
|
||||||
structureSetupState._appendElements = () => {
|
structureSetupState._appendElements = () => {
|
||||||
const { _target, _viewport } = elements;
|
const { _target, _viewport } = elements;
|
||||||
const initialScrollLeft = scrollLeft(_target);
|
const initialScrollLeft = scrollLeft(_target);
|
||||||
@@ -113,7 +112,7 @@ export const createStructureSetup = (
|
|||||||
(changedOptions, force?) => {
|
(changedOptions, force?) => {
|
||||||
const checkOption = createOptionCheck(options, changedOptions, force);
|
const checkOption = createOptionCheck(options, changedOptions, force);
|
||||||
updateObserversOptions(checkOption);
|
updateObserversOptions(checkOption);
|
||||||
triggerUpdateEvent(
|
return triggerUpdateEvent(
|
||||||
updateStructure(checkOption, updateObservers(), force),
|
updateStructure(checkOption, updateObservers(), force),
|
||||||
changedOptions,
|
changedOptions,
|
||||||
!!force
|
!!force
|
||||||
|
|||||||
@@ -0,0 +1,408 @@
|
|||||||
|
import { DeepPartial } from 'typings';
|
||||||
|
import { defaultOptions, Options } from 'options';
|
||||||
|
import { assignDeep } from 'support';
|
||||||
|
import { OverlayScrollbars as originalOverlayScrollbars } from '../../src/overlayscrollbars';
|
||||||
|
|
||||||
|
const bodyElm = document.body;
|
||||||
|
const div = document.createElement('div');
|
||||||
|
|
||||||
|
bodyElm.append(div);
|
||||||
|
|
||||||
|
let OverlayScrollbars = originalOverlayScrollbars;
|
||||||
|
|
||||||
|
describe('overlayscrollbars', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.resetModules();
|
||||||
|
({ OverlayScrollbars } = await import('../../src/overlayscrollbars'));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
const instance = OverlayScrollbars(div);
|
||||||
|
if (OverlayScrollbars.valid(instance)) {
|
||||||
|
instance.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('instance', () => {
|
||||||
|
describe('initialization', () => {
|
||||||
|
describe('initialization completed', () => {
|
||||||
|
[div, { target: div }].forEach((init) => {
|
||||||
|
describe(`as ${init === div ? 'element' : 'object'}`, () => {
|
||||||
|
test('without options', () => {
|
||||||
|
const osInstance = OverlayScrollbars(init);
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false);
|
||||||
|
expect(div.children.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with empty options', () => {
|
||||||
|
const osInstance = OverlayScrollbars(init, {});
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
||||||
|
expect(div.children.length).not.toBe(0);
|
||||||
|
|
||||||
|
expect(osInstance.options()).not.toBe(defaultOptions);
|
||||||
|
expect(osInstance.options()).toEqual(defaultOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with custom options', () => {
|
||||||
|
const customOptions: DeepPartial<Options> = {
|
||||||
|
paddingAbsolute: false,
|
||||||
|
showNativeOverlaidScrollbars: true,
|
||||||
|
overflow: {
|
||||||
|
x: 'hidden',
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
ignoreMutation: () => true,
|
||||||
|
elementEvents: null,
|
||||||
|
},
|
||||||
|
scrollbars: {
|
||||||
|
pointers: null,
|
||||||
|
autoHideDelay: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const osInstance = OverlayScrollbars(init, customOptions);
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
||||||
|
expect(div.children.length).not.toBe(0);
|
||||||
|
|
||||||
|
expect(osInstance.options()).toEqual(assignDeep({}, defaultOptions, customOptions));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with event listeners', () => {
|
||||||
|
const initialized = jest.fn();
|
||||||
|
const updated = jest.fn();
|
||||||
|
const destroyed = jest.fn();
|
||||||
|
const osInstance = OverlayScrollbars(
|
||||||
|
init,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
initialized,
|
||||||
|
updated,
|
||||||
|
destroyed,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
||||||
|
expect(div.children.length).not.toBe(0);
|
||||||
|
|
||||||
|
expect(initialized).toHaveBeenCalledTimes(1);
|
||||||
|
expect(initialized).toHaveBeenCalledWith(osInstance);
|
||||||
|
|
||||||
|
expect(updated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(updated).toHaveBeenCalledWith(osInstance, expect.any(Object));
|
||||||
|
|
||||||
|
expect(destroyed).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
osInstance.destroy();
|
||||||
|
|
||||||
|
expect(destroyed).toHaveBeenCalledTimes(1);
|
||||||
|
expect(destroyed).toHaveBeenCalledWith(osInstance, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('initialization canceled', () => {
|
||||||
|
test('without options', () => {
|
||||||
|
const osInstance = OverlayScrollbars({
|
||||||
|
target: div,
|
||||||
|
cancel: { nativeScrollbarsOverlaid: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false);
|
||||||
|
expect(div.children.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with empty options', () => {
|
||||||
|
const osInstance = OverlayScrollbars(
|
||||||
|
{
|
||||||
|
target: div,
|
||||||
|
cancel: { nativeScrollbarsOverlaid: true },
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false);
|
||||||
|
expect(div.children.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with event listeners', () => {
|
||||||
|
const initialized = jest.fn();
|
||||||
|
const updated = jest.fn();
|
||||||
|
const destroyed = jest.fn();
|
||||||
|
const osInstance = OverlayScrollbars(
|
||||||
|
{
|
||||||
|
target: div,
|
||||||
|
cancel: { nativeScrollbarsOverlaid: true },
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
initialized,
|
||||||
|
updated,
|
||||||
|
destroyed,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false);
|
||||||
|
expect(div.children.length).toBe(0);
|
||||||
|
|
||||||
|
expect(initialized).not.toHaveBeenCalled();
|
||||||
|
expect(updated).not.toHaveBeenCalled();
|
||||||
|
expect(destroyed).toHaveBeenCalledTimes(1);
|
||||||
|
expect(destroyed).toHaveBeenCalledWith(osInstance, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('options', () => {
|
||||||
|
const customOptions: DeepPartial<Options> = {
|
||||||
|
paddingAbsolute: !defaultOptions.paddingAbsolute,
|
||||||
|
overflow: { x: 'hidden' },
|
||||||
|
};
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
expect(osInstance.options()).not.toBe(defaultOptions);
|
||||||
|
expect(osInstance.options()).toEqual(defaultOptions);
|
||||||
|
OverlayScrollbars(div)!.destroy();
|
||||||
|
|
||||||
|
expect(OverlayScrollbars(div, customOptions).options()).toEqual(
|
||||||
|
assignDeep({}, defaultOptions, customOptions)
|
||||||
|
);
|
||||||
|
OverlayScrollbars(div)!.destroy();
|
||||||
|
|
||||||
|
const osInstance2 = OverlayScrollbars(div, {});
|
||||||
|
expect(osInstance2.options(customOptions)).toEqual(
|
||||||
|
assignDeep({}, defaultOptions, customOptions)
|
||||||
|
);
|
||||||
|
expect(osInstance2.options()).toEqual(assignDeep({}, defaultOptions, customOptions));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('on', () => {
|
||||||
|
const onInitialized = jest.fn();
|
||||||
|
const onUpdated = jest.fn();
|
||||||
|
const onUpdated2 = jest.fn();
|
||||||
|
const onDestroyed = jest.fn();
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
|
||||||
|
osInstance.on('initialized', onInitialized);
|
||||||
|
osInstance.on('updated', [onUpdated, onUpdated, onUpdated2]);
|
||||||
|
osInstance.on('destroyed', onDestroyed);
|
||||||
|
|
||||||
|
expect(onInitialized).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(onUpdated).not.toHaveBeenCalled();
|
||||||
|
expect(onUpdated2).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
osInstance.update();
|
||||||
|
expect(onUpdated).not.toHaveBeenCalled();
|
||||||
|
expect(onUpdated2).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
osInstance.update(true);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onUpdated2).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onUpdated).toHaveBeenLastCalledWith(osInstance, expect.any(Object));
|
||||||
|
expect(onUpdated2).toHaveBeenLastCalledWith(osInstance, expect.any(Object));
|
||||||
|
|
||||||
|
expect(onDestroyed).not.toHaveBeenCalled();
|
||||||
|
OverlayScrollbars(div)!.destroy();
|
||||||
|
expect(onDestroyed).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onDestroyed).toHaveBeenLastCalledWith(osInstance, false);
|
||||||
|
|
||||||
|
// after destruction no further events are triggered
|
||||||
|
osInstance.update(true);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onUpdated2).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('off', () => {
|
||||||
|
const onInitialized = jest.fn();
|
||||||
|
const onUpdated = jest.fn();
|
||||||
|
const onUpdated2 = jest.fn();
|
||||||
|
const onDestroyed = jest.fn();
|
||||||
|
|
||||||
|
expect(onInitialized).not.toHaveBeenCalled();
|
||||||
|
const osInstance = OverlayScrollbars(
|
||||||
|
div,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
initialized: onInitialized,
|
||||||
|
updated: [onUpdated, onUpdated, onUpdated2],
|
||||||
|
destroyed: onDestroyed,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onInitialized).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onInitialized).toHaveBeenLastCalledWith(osInstance);
|
||||||
|
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onUpdated2).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
osInstance.update(true);
|
||||||
|
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(2);
|
||||||
|
expect(onUpdated2).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
osInstance.off('updated', onUpdated2);
|
||||||
|
osInstance.update(true);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(3);
|
||||||
|
expect(onUpdated2).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
osInstance.on('updated', onUpdated2);
|
||||||
|
osInstance.update(true);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(4);
|
||||||
|
expect(onUpdated2).toHaveBeenCalledTimes(3);
|
||||||
|
|
||||||
|
osInstance.off('updated', [onUpdated, onUpdated2]);
|
||||||
|
osInstance.update(true);
|
||||||
|
expect(onUpdated).toHaveBeenCalledTimes(4);
|
||||||
|
expect(onUpdated2).toHaveBeenCalledTimes(3);
|
||||||
|
|
||||||
|
osInstance.off('destroyed', onDestroyed);
|
||||||
|
expect(onDestroyed).not.toHaveBeenCalled();
|
||||||
|
osInstance.destroy();
|
||||||
|
expect(onDestroyed).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('state', () => {
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
const state = osInstance.state();
|
||||||
|
const stateObj = {
|
||||||
|
overflowEdge: { x: 0, y: 0 },
|
||||||
|
overflowAmount: { x: 0, y: 0 },
|
||||||
|
overflowStyle: { x: '', y: '' },
|
||||||
|
hasOverflow: { x: false, y: false },
|
||||||
|
padding: { t: 0, r: 0, b: 0, l: 0 },
|
||||||
|
paddingAbsolute: false,
|
||||||
|
directionRTL: false,
|
||||||
|
destroyed: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(state).not.toBe(osInstance.state());
|
||||||
|
expect(state).toEqual(osInstance.state());
|
||||||
|
expect(state).toEqual(stateObj);
|
||||||
|
|
||||||
|
osInstance.options({ paddingAbsolute: true });
|
||||||
|
expect(osInstance.state()).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
paddingAbsolute: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
osInstance.destroy();
|
||||||
|
expect(osInstance.state()).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
destroyed: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('elements', () => {
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
const elements = osInstance.elements();
|
||||||
|
const elementsObj = {
|
||||||
|
target: div,
|
||||||
|
host: div,
|
||||||
|
padding: expect.any(HTMLElement),
|
||||||
|
viewport: expect.any(HTMLElement),
|
||||||
|
content: expect.any(HTMLElement),
|
||||||
|
scrollOffsetElement: expect.any(HTMLElement),
|
||||||
|
scrollEventElement: expect.any(HTMLElement),
|
||||||
|
scrollbarHorizontal: {
|
||||||
|
scrollbar: expect.any(HTMLElement),
|
||||||
|
track: expect.any(HTMLElement),
|
||||||
|
handle: expect.any(HTMLElement),
|
||||||
|
clone: expect.any(Function),
|
||||||
|
},
|
||||||
|
scrollbarVertical: {
|
||||||
|
scrollbar: expect.any(HTMLElement),
|
||||||
|
track: expect.any(HTMLElement),
|
||||||
|
handle: expect.any(HTMLElement),
|
||||||
|
clone: expect.any(Function),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(elements).not.toBe(osInstance.elements());
|
||||||
|
// clone function identity is always different
|
||||||
|
expect(
|
||||||
|
assignDeep({}, elements, {
|
||||||
|
scrollbarHorizontal: {
|
||||||
|
clone: null,
|
||||||
|
},
|
||||||
|
scrollbarVertical: {
|
||||||
|
clone: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).toEqual(
|
||||||
|
assignDeep({}, osInstance.elements(), {
|
||||||
|
scrollbarHorizontal: {
|
||||||
|
clone: null,
|
||||||
|
},
|
||||||
|
scrollbarVertical: {
|
||||||
|
clone: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(elements).toEqual(elementsObj);
|
||||||
|
|
||||||
|
osInstance.destroy();
|
||||||
|
|
||||||
|
expect(elements).toEqual(elementsObj);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('update', () => {
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
expect(osInstance.update()).toBe(false);
|
||||||
|
expect(osInstance.update(false)).toBe(false);
|
||||||
|
expect(osInstance.update(true)).toBe(true);
|
||||||
|
|
||||||
|
// host mutation
|
||||||
|
div.style.cursor = 'pointer';
|
||||||
|
expect(osInstance.update()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('static', () => {
|
||||||
|
test('plugin', () => {
|
||||||
|
expect(OverlayScrollbars.plugin).toEqual(expect.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('env', () => {
|
||||||
|
const env = OverlayScrollbars.env();
|
||||||
|
const envObj = {
|
||||||
|
scrollbarsSize: { x: 0, y: 0 },
|
||||||
|
scrollbarsOverlaid: { x: true, y: true },
|
||||||
|
scrollbarsHiding: false,
|
||||||
|
rtlScrollBehavior: { i: true, n: false },
|
||||||
|
flexboxGlue: true,
|
||||||
|
cssCustomProperties: false,
|
||||||
|
staticDefaultInitialization: expect.any(Object),
|
||||||
|
staticDefaultOptions: expect.any(Object),
|
||||||
|
getDefaultInitialization: expect.any(Function),
|
||||||
|
setDefaultInitialization: expect.any(Function),
|
||||||
|
getDefaultOptions: expect.any(Function),
|
||||||
|
setDefaultOptions: expect.any(Function),
|
||||||
|
};
|
||||||
|
expect(env).not.toBe(OverlayScrollbars.env());
|
||||||
|
expect(env).toEqual(OverlayScrollbars.env());
|
||||||
|
expect(env).toEqual(envObj);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('valid', () => {
|
||||||
|
expect(OverlayScrollbars.valid(true)).toBe(false);
|
||||||
|
expect(OverlayScrollbars.valid(false)).toBe(false);
|
||||||
|
expect(OverlayScrollbars.valid('')).toBe(false);
|
||||||
|
expect(OverlayScrollbars.valid(123)).toBe(false);
|
||||||
|
expect(OverlayScrollbars.valid({})).toBe(false);
|
||||||
|
expect(OverlayScrollbars.valid(div)).toBe(false);
|
||||||
|
expect(OverlayScrollbars.valid(setTimeout)).toBe(false);
|
||||||
|
|
||||||
|
expect(OverlayScrollbars.valid(OverlayScrollbars(div))).toBe(false);
|
||||||
|
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
||||||
|
|
||||||
|
osInstance.destroy();
|
||||||
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,6 +2,7 @@ import { createEventListenerHub } from 'support/eventListeners';
|
|||||||
|
|
||||||
type EventMap = {
|
type EventMap = {
|
||||||
onBoolean: [a: boolean, b: string];
|
onBoolean: [a: boolean, b: string];
|
||||||
|
onString: [a: string];
|
||||||
onUndefined: [];
|
onUndefined: [];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -10,19 +11,27 @@ describe('eventListeners', () => {
|
|||||||
test('initialization', () => {
|
test('initialization', () => {
|
||||||
const onBooleanA = jest.fn((a: boolean, b: string) => a + b);
|
const onBooleanA = jest.fn((a: boolean, b: string) => a + b);
|
||||||
const onBooleanB = jest.fn();
|
const onBooleanB = jest.fn();
|
||||||
|
const onString = jest.fn();
|
||||||
const onUndefined = jest.fn();
|
const onUndefined = jest.fn();
|
||||||
const [, , triggerEvent] = createEventListenerHub<EventMap>({
|
const [, , triggerEvent] = createEventListenerHub<EventMap>({
|
||||||
onBoolean: [onBooleanA, onBooleanB],
|
onBoolean: [onBooleanA, onBooleanB],
|
||||||
|
onString: [onString, onString], // multiple equal listeners are treated as one
|
||||||
onUndefined,
|
onUndefined,
|
||||||
});
|
});
|
||||||
triggerEvent('onBoolean', [true, 'hi']);
|
triggerEvent('onBoolean', [true, 'hi']);
|
||||||
|
triggerEvent('onString', ['hi']);
|
||||||
triggerEvent('onUndefined');
|
triggerEvent('onUndefined');
|
||||||
|
|
||||||
expect(onBooleanA).toHaveBeenCalledTimes(1);
|
expect(onBooleanA).toHaveBeenCalledTimes(1);
|
||||||
expect(onBooleanA).toHaveBeenLastCalledWith(true, 'hi');
|
expect(onBooleanA).toHaveBeenLastCalledWith(true, 'hi');
|
||||||
|
|
||||||
expect(onBooleanB).toHaveBeenCalledTimes(1);
|
expect(onBooleanB).toHaveBeenCalledTimes(1);
|
||||||
expect(onBooleanB).toHaveBeenLastCalledWith(true, 'hi');
|
expect(onBooleanB).toHaveBeenLastCalledWith(true, 'hi');
|
||||||
|
|
||||||
|
// even though onString was registered twice, its only called once
|
||||||
|
expect(onString).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onString).toHaveBeenLastCalledWith('hi');
|
||||||
|
|
||||||
expect(onUndefined).toHaveBeenCalledTimes(1);
|
expect(onUndefined).toHaveBeenCalledTimes(1);
|
||||||
expect(onUndefined).toHaveBeenLastCalledWith();
|
expect(onUndefined).toHaveBeenLastCalledWith();
|
||||||
});
|
});
|
||||||
@@ -30,6 +39,7 @@ describe('eventListeners', () => {
|
|||||||
test('addEvent', () => {
|
test('addEvent', () => {
|
||||||
const onBooleanA = jest.fn((a: boolean, b: string) => a + b);
|
const onBooleanA = jest.fn((a: boolean, b: string) => a + b);
|
||||||
const onBooleanB = jest.fn();
|
const onBooleanB = jest.fn();
|
||||||
|
const onString = jest.fn();
|
||||||
const onUndefinedA = jest.fn();
|
const onUndefinedA = jest.fn();
|
||||||
const onUndefinedB = jest.fn();
|
const onUndefinedB = jest.fn();
|
||||||
const [addEvent, , triggerEvent] = createEventListenerHub<EventMap>();
|
const [addEvent, , triggerEvent] = createEventListenerHub<EventMap>();
|
||||||
@@ -68,6 +78,14 @@ describe('eventListeners', () => {
|
|||||||
expect(onUndefinedB).toHaveBeenCalledTimes(2);
|
expect(onUndefinedB).toHaveBeenCalledTimes(2);
|
||||||
expect(onUndefinedB).toHaveBeenLastCalledWith();
|
expect(onUndefinedB).toHaveBeenLastCalledWith();
|
||||||
|
|
||||||
|
// multiple equal listeners are treated as one
|
||||||
|
addEvent('onString', [onString, onString]);
|
||||||
|
triggerEvent('onString', ['hi']);
|
||||||
|
|
||||||
|
// even though onString was registered twice, its only called once
|
||||||
|
expect(onString).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onString).toHaveBeenLastCalledWith('hi');
|
||||||
|
|
||||||
const something = jest.fn();
|
const something = jest.fn();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
addEvent('something', something);
|
addEvent('something', something);
|
||||||
@@ -79,6 +97,7 @@ describe('eventListeners', () => {
|
|||||||
test('removeEvent', () => {
|
test('removeEvent', () => {
|
||||||
const onBooleanA = jest.fn();
|
const onBooleanA = jest.fn();
|
||||||
const onBooleanB = jest.fn();
|
const onBooleanB = jest.fn();
|
||||||
|
const onString = jest.fn();
|
||||||
const onUndefinedA = jest.fn();
|
const onUndefinedA = jest.fn();
|
||||||
const onUndefinedB = jest.fn();
|
const onUndefinedB = jest.fn();
|
||||||
const [addEvent, removeEvent, triggerEvent] = createEventListenerHub<EventMap>({
|
const [addEvent, removeEvent, triggerEvent] = createEventListenerHub<EventMap>({
|
||||||
@@ -149,6 +168,17 @@ describe('eventListeners', () => {
|
|||||||
expect(onUndefinedA).toHaveBeenCalledTimes(4);
|
expect(onUndefinedA).toHaveBeenCalledTimes(4);
|
||||||
expect(onUndefinedB).toHaveBeenCalledTimes(4);
|
expect(onUndefinedB).toHaveBeenCalledTimes(4);
|
||||||
|
|
||||||
|
addEvent('onString', [onString, onString]);
|
||||||
|
triggerEvent('onString', ['hi']);
|
||||||
|
|
||||||
|
expect(onString).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
// even though onString was registered twice, its treated as a single listener because multiple equal listeners are treated as one
|
||||||
|
removeEvent('onString', onString);
|
||||||
|
triggerEvent('onString', ['hi']);
|
||||||
|
|
||||||
|
expect(onString).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const something = jest.fn();
|
const something = jest.fn();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
Reference in New Issue
Block a user