mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-20 07:50:36 +03:00
improve plugin system & add and improve various tests
This commit is contained in:
@@ -1,4 +1,13 @@
|
|||||||
import { assignDeep, each, isObject, keys, isArray, hasOwnProperty, isFunction } from 'support';
|
import {
|
||||||
|
assignDeep,
|
||||||
|
each,
|
||||||
|
isObject,
|
||||||
|
keys,
|
||||||
|
isArray,
|
||||||
|
hasOwnProperty,
|
||||||
|
isFunction,
|
||||||
|
isEmptyObject,
|
||||||
|
} from 'support';
|
||||||
import { DeepPartial, DeepReadonly } from 'typings';
|
import { DeepPartial, DeepReadonly } from 'typings';
|
||||||
|
|
||||||
const opsStringify = (value: any) =>
|
const opsStringify = (value: any) =>
|
||||||
@@ -80,6 +89,10 @@ export const getOptionsDiff = <T>(currOptions: T, newOptions: DeepPartial<T>): D
|
|||||||
|
|
||||||
if (isObject(currOptionValue) && isObject(newOptionValue)) {
|
if (isObject(currOptionValue) && isObject(newOptionValue)) {
|
||||||
assignDeep((diff[optionKey] = {}), getOptionsDiff(currOptionValue, newOptionValue));
|
assignDeep((diff[optionKey] = {}), getOptionsDiff(currOptionValue, newOptionValue));
|
||||||
|
// delete empty nested objects
|
||||||
|
if (isEmptyObject(diff[optionKey])) {
|
||||||
|
delete diff[optionKey];
|
||||||
|
}
|
||||||
} else if (hasOwnProperty(newOptions, optionKey) && newOptionValue !== currOptionValue) {
|
} else if (hasOwnProperty(newOptions, optionKey) && newOptionValue !== currOptionValue) {
|
||||||
let isDiff = true;
|
let isDiff = true;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { getEnvironment } from 'environment';
|
|||||||
import { cancelInitialization } from 'initialization';
|
import { cancelInitialization } from 'initialization';
|
||||||
import { addInstance, getInstance, removeInstance } from 'instances';
|
import { addInstance, getInstance, removeInstance } from 'instances';
|
||||||
import { createStructureSetup, createScrollbarsSetup } from 'setups';
|
import { createStructureSetup, createScrollbarsSetup } from 'setups';
|
||||||
import { getPlugins, addPlugin, optionsValidationPluginName } from 'plugins';
|
import { getPlugins, addPlugin, optionsValidationPluginName, PluginInstance } from 'plugins';
|
||||||
import type { XY, TRBL } from 'support';
|
import type { XY, TRBL } from 'support';
|
||||||
import type { Options, ReadonlyOptions } from 'options';
|
import type { Options, ReadonlyOptions } from 'options';
|
||||||
import type { Plugin, OptionsValidationPluginInstance } from 'plugins';
|
import type { Plugin, OptionsValidationPluginInstance } from 'plugins';
|
||||||
@@ -109,6 +109,16 @@ export interface OverlayScrollbars {
|
|||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const invokePluginInstance = (
|
||||||
|
pluginInstance: PluginInstance,
|
||||||
|
staticObj?: OverlayScrollbarsStatic | false | null | undefined | 0,
|
||||||
|
instanceObj?: OverlayScrollbars | false | null | undefined | 0
|
||||||
|
) => {
|
||||||
|
if (isFunction(pluginInstance)) {
|
||||||
|
pluginInstance(staticObj || undefined, instanceObj || undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
||||||
export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
||||||
target: InitializationTarget,
|
target: InitializationTarget,
|
||||||
@@ -122,13 +132,12 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
const potentialInstance = getInstance(instanceTarget);
|
const potentialInstance = getInstance(instanceTarget);
|
||||||
if (options && !potentialInstance) {
|
if (options && !potentialInstance) {
|
||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
const optionsValidationPlugin = plugins[
|
const validateOptions = (newOptions: DeepPartial<Options>) => {
|
||||||
optionsValidationPluginName
|
const optionsValidationPlugin = getPlugins()[
|
||||||
] as OptionsValidationPluginInstance;
|
optionsValidationPluginName
|
||||||
const validateOptions = (newOptions?: DeepPartial<Options>) => {
|
] as OptionsValidationPluginInstance;
|
||||||
const opts = newOptions || {};
|
|
||||||
const validate = optionsValidationPlugin && optionsValidationPlugin._;
|
const validate = optionsValidationPlugin && optionsValidationPlugin._;
|
||||||
return validate ? validate(opts, true) : opts;
|
return validate ? validate(newOptions, true) : newOptions;
|
||||||
};
|
};
|
||||||
const currentOptions: ReadonlyOptions = assignDeep(
|
const currentOptions: ReadonlyOptions = assignDeep(
|
||||||
{},
|
{},
|
||||||
@@ -166,7 +175,6 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
options(newOptions?: DeepPartial<Options>) {
|
options(newOptions?: DeepPartial<Options>) {
|
||||||
if (newOptions) {
|
if (newOptions) {
|
||||||
const changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions));
|
const changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions));
|
||||||
|
|
||||||
if (!isEmptyObject(changedOptions)) {
|
if (!isEmptyObject(changedOptions)) {
|
||||||
assignDeep(currentOptions, changedOptions);
|
assignDeep(currentOptions, changedOptions);
|
||||||
update(changedOptions);
|
update(changedOptions);
|
||||||
@@ -260,12 +268,11 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
updateScrollbars(changedOptions, force, updateHints);
|
updateScrollbars(changedOptions, force, updateHints);
|
||||||
});
|
});
|
||||||
|
|
||||||
each(keys(plugins), (pluginName) => {
|
// valid inside plugins
|
||||||
const pluginInstance = plugins[pluginName];
|
addInstance(instanceTarget, instance);
|
||||||
if (isFunction(pluginInstance)) {
|
|
||||||
pluginInstance(OverlayScrollbars, instance);
|
// init plugins
|
||||||
}
|
each(keys(plugins), (pluginName) => invokePluginInstance(plugins[pluginName], 0, instance));
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
cancelInitialization(
|
cancelInitialization(
|
||||||
@@ -281,7 +288,6 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
structureState._appendElements();
|
structureState._appendElements();
|
||||||
scrollbarsState._appendElements();
|
scrollbarsState._appendElements();
|
||||||
|
|
||||||
addInstance(instanceTarget, instance);
|
|
||||||
triggerEvent('initialized', [instance]);
|
triggerEvent('initialized', [instance]);
|
||||||
|
|
||||||
structureState._addOnUpdatedListener((updateHints, changedOptions, force) => {
|
structureState._addOnUpdatedListener((updateHints, changedOptions, force) => {
|
||||||
@@ -322,7 +328,11 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
return potentialInstance!;
|
return potentialInstance!;
|
||||||
};
|
};
|
||||||
|
|
||||||
OverlayScrollbars.plugin = addPlugin;
|
OverlayScrollbars.plugin = (plugins: Plugin | Plugin[]) => {
|
||||||
|
each(addPlugin(plugins), (pluginInstance) =>
|
||||||
|
invokePluginInstance(pluginInstance, OverlayScrollbars)
|
||||||
|
);
|
||||||
|
};
|
||||||
OverlayScrollbars.valid = (osInstance: any): osInstance is OverlayScrollbars => {
|
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();
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { each, isArray, keys } from 'support';
|
import { each, isArray, keys, push } from 'support';
|
||||||
import { OverlayScrollbars, OverlayScrollbarsStatic } from 'overlayscrollbars';
|
import { OverlayScrollbars, OverlayScrollbarsStatic } from 'overlayscrollbars';
|
||||||
|
|
||||||
export type PluginInstance =
|
export type PluginInstance =
|
||||||
| Record<string, unknown>
|
| Record<string, unknown>
|
||||||
| ((staticObj: OverlayScrollbarsStatic, instanceObj: OverlayScrollbars) => void);
|
| ((staticObj?: OverlayScrollbarsStatic, instanceObj?: OverlayScrollbars) => void);
|
||||||
export type Plugin<T extends PluginInstance = PluginInstance> = {
|
export type Plugin<T extends PluginInstance = PluginInstance> = {
|
||||||
[pluginName: string]: T;
|
[pluginName: string]: T;
|
||||||
};
|
};
|
||||||
@@ -12,9 +12,14 @@ const pluginRegistry: Record<string, PluginInstance> = {};
|
|||||||
|
|
||||||
export const getPlugins = () => pluginRegistry;
|
export const getPlugins = () => pluginRegistry;
|
||||||
|
|
||||||
export const addPlugin = (addedPlugin: Plugin | Plugin[]): void => {
|
export const addPlugin = (addedPlugin: Plugin | Plugin[]): Plugin[] => {
|
||||||
|
const result: Plugin[] = [];
|
||||||
each((isArray(addedPlugin) ? addedPlugin : [addedPlugin]) as Plugin[], (plugin) => {
|
each((isArray(addedPlugin) ? addedPlugin : [addedPlugin]) as Plugin[], (plugin) => {
|
||||||
const pluginName = keys(plugin)[0];
|
// multiple "sub-plugins" per plugin object possible to support "static", "instanceObj" and "staticObj" sub-plugins per plugin
|
||||||
pluginRegistry[pluginName] = plugin[pluginName];
|
const pluginNameKeys = keys(plugin);
|
||||||
|
each(pluginNameKeys, (key) => {
|
||||||
|
push(result, (pluginRegistry[key] = plugin[key]));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import type {
|
|||||||
} from 'initialization';
|
} from 'initialization';
|
||||||
|
|
||||||
export type StructureSetupElements = [
|
export type StructureSetupElements = [
|
||||||
targetObj: StructureSetupElementsObj,
|
elements: StructureSetupElementsObj,
|
||||||
appendElements: () => void,
|
appendElements: () => void,
|
||||||
destroy: () => void
|
destroy: () => void
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ describe('options', () => {
|
|||||||
b: 0,
|
b: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
expect(getOptionsDiff(options, options)).toEqual({});
|
||||||
expect(getOptionsDiff(options, changed)).toEqual(changed);
|
expect(getOptionsDiff(options, changed)).toEqual(changed);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -74,6 +75,7 @@ describe('options', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
expect(getOptionsDiff(options, options)).toEqual({});
|
||||||
expect(getOptionsDiff(options, changed)).toEqual(changed);
|
expect(getOptionsDiff(options, changed)).toEqual(changed);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { DeepPartial } from 'typings';
|
import { DeepPartial } from 'typings';
|
||||||
import { defaultOptions, Options } from 'options';
|
import { defaultOptions, Options } from 'options';
|
||||||
import { assignDeep } from 'support';
|
import { assignDeep } from 'support';
|
||||||
|
import { optionsValidationPlugin } from 'plugins';
|
||||||
import { OverlayScrollbars as originalOverlayScrollbars } from '../../src/overlayscrollbars';
|
import { OverlayScrollbars as originalOverlayScrollbars } from '../../src/overlayscrollbars';
|
||||||
|
|
||||||
const bodyElm = document.body;
|
const bodyElm = document.body;
|
||||||
@@ -155,26 +156,144 @@ describe('overlayscrollbars', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('options', () => {
|
describe('elements', () => {
|
||||||
const customOptions: DeepPartial<Options> = {
|
test('get elements', () => {
|
||||||
paddingAbsolute: !defaultOptions.paddingAbsolute,
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
overflow: { x: 'hidden' },
|
const elements = osInstance.elements();
|
||||||
};
|
const elementsObj = {
|
||||||
const osInstance = OverlayScrollbars(div, {});
|
target: div,
|
||||||
expect(osInstance.options()).not.toBe(defaultOptions);
|
host: div,
|
||||||
expect(osInstance.options()).toEqual(defaultOptions);
|
padding: expect.any(HTMLElement),
|
||||||
OverlayScrollbars(div)!.destroy();
|
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(OverlayScrollbars(div, customOptions).options()).toEqual(
|
expect(elements).not.toBe(osInstance.elements());
|
||||||
assignDeep({}, defaultOptions, customOptions)
|
// clone function identity is always different
|
||||||
);
|
expect(
|
||||||
OverlayScrollbars(div)!.destroy();
|
assignDeep({}, elements, {
|
||||||
|
scrollbarHorizontal: {
|
||||||
|
clone: null,
|
||||||
|
},
|
||||||
|
scrollbarVertical: {
|
||||||
|
clone: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).toEqual(
|
||||||
|
assignDeep({}, osInstance.elements(), {
|
||||||
|
scrollbarHorizontal: {
|
||||||
|
clone: null,
|
||||||
|
},
|
||||||
|
scrollbarVertical: {
|
||||||
|
clone: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(elements).toEqual(elementsObj);
|
||||||
|
|
||||||
const osInstance2 = OverlayScrollbars(div, {});
|
osInstance.destroy();
|
||||||
expect(osInstance2.options(customOptions)).toEqual(
|
|
||||||
assignDeep({}, defaultOptions, customOptions)
|
expect(elements).toEqual(elementsObj);
|
||||||
);
|
});
|
||||||
expect(osInstance2.options()).toEqual(assignDeep({}, defaultOptions, customOptions));
|
|
||||||
|
test('clone scrollbars', () => {
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
const elements = osInstance.elements();
|
||||||
|
|
||||||
|
const hClone = elements.scrollbarHorizontal.clone();
|
||||||
|
const vClone = elements.scrollbarVertical.clone();
|
||||||
|
|
||||||
|
expect(hClone).toEqual({
|
||||||
|
scrollbar: expect.any(HTMLElement),
|
||||||
|
track: expect.any(HTMLElement),
|
||||||
|
handle: expect.any(HTMLElement),
|
||||||
|
});
|
||||||
|
expect(vClone).toEqual({
|
||||||
|
scrollbar: expect.any(HTMLElement),
|
||||||
|
track: expect.any(HTMLElement),
|
||||||
|
handle: expect.any(HTMLElement),
|
||||||
|
});
|
||||||
|
|
||||||
|
div.append(hClone.scrollbar);
|
||||||
|
div.append(vClone.scrollbar);
|
||||||
|
|
||||||
|
osInstance.destroy();
|
||||||
|
|
||||||
|
expect(div.innerHTML).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('options', () => {
|
||||||
|
[false, true].forEach((withValidationPlugin) => {
|
||||||
|
describe(`${withValidationPlugin ? 'with' : 'without'} optionsValidationPlugin`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
if (withValidationPlugin) {
|
||||||
|
OverlayScrollbars.plugin(optionsValidationPlugin);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('equality', () => {
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
expect(osInstance.options()).not.toBe(defaultOptions);
|
||||||
|
expect(osInstance.options()).toEqual(defaultOptions);
|
||||||
|
OverlayScrollbars(div)!.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('initial options', () => {
|
||||||
|
const customOptions: DeepPartial<Options> = {
|
||||||
|
paddingAbsolute: !defaultOptions.paddingAbsolute,
|
||||||
|
overflow: { x: 'hidden' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const osInstance = OverlayScrollbars(div, customOptions);
|
||||||
|
expect(osInstance.options()).toEqual(assignDeep({}, defaultOptions, customOptions));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('changed options', () => {
|
||||||
|
const customOptions: DeepPartial<Options> = {
|
||||||
|
paddingAbsolute: !defaultOptions.paddingAbsolute,
|
||||||
|
overflow: { x: 'hidden' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
expect(osInstance.options(customOptions)).toEqual(
|
||||||
|
assignDeep({}, defaultOptions, customOptions)
|
||||||
|
);
|
||||||
|
expect(osInstance.options()).toEqual(assignDeep({}, defaultOptions, customOptions));
|
||||||
|
osInstance.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unchanged wont trigger update', () => {
|
||||||
|
const updated = jest.fn();
|
||||||
|
const initialOpts: DeepPartial<Options> = {
|
||||||
|
paddingAbsolute: true,
|
||||||
|
overflow: {
|
||||||
|
y: 'hidden',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const osInstance4 = OverlayScrollbars(div, initialOpts, {
|
||||||
|
updated,
|
||||||
|
});
|
||||||
|
expect(updated).toHaveBeenCalledTimes(1);
|
||||||
|
osInstance4.options(initialOpts);
|
||||||
|
expect(updated).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('on', () => {
|
test('on', () => {
|
||||||
@@ -296,59 +415,6 @@ describe('overlayscrollbars', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
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', () => {
|
test('update', () => {
|
||||||
const osInstance = OverlayScrollbars(div, {});
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
expect(osInstance.update()).toBe(false);
|
expect(osInstance.update()).toBe(false);
|
||||||
@@ -405,4 +471,64 @@ describe('overlayscrollbars', () => {
|
|||||||
expect(OverlayScrollbars.valid(osInstance)).toBe(false);
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('plugins', () => {
|
||||||
|
const staticPluginFn = jest.fn();
|
||||||
|
const staticObjPluginFn = jest.fn();
|
||||||
|
const instanceObjPluginFn = jest.fn();
|
||||||
|
const staticPlugin = {
|
||||||
|
staticPlugin: {
|
||||||
|
staticFn: staticPluginFn,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
OverlayScrollbars.plugin([
|
||||||
|
{
|
||||||
|
expect: (staticObj, instanceObj) => {
|
||||||
|
if (instanceObj) {
|
||||||
|
expect(staticObj).toBe(undefined);
|
||||||
|
expect(OverlayScrollbars.valid(instanceObj)).toBe(true);
|
||||||
|
}
|
||||||
|
if (staticObj) {
|
||||||
|
expect(staticObj).toBe(OverlayScrollbars);
|
||||||
|
expect(instanceObj).toBe(undefined);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
staticPlugin,
|
||||||
|
staticObjPlugin: (staticObj, instanceObj) => {
|
||||||
|
if (staticObj) {
|
||||||
|
expect(instanceObj).toBe(undefined);
|
||||||
|
// @ts-ignore
|
||||||
|
staticObj.staticObjPluginFn = staticObjPluginFn;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
instanceObjPlugin: (_, instanceObj) => {
|
||||||
|
if (instanceObj) {
|
||||||
|
// @ts-ignore
|
||||||
|
instanceObj.instanceObjPluginFn = instanceObjPluginFn;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).toBe(undefined);
|
||||||
|
|
||||||
|
expect(staticPluginFn).not.toHaveBeenCalled();
|
||||||
|
// @ts-ignore
|
||||||
|
staticPlugin.staticPlugin.staticFn();
|
||||||
|
expect(staticPluginFn).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
// staticObj plugin must be available before any initialization
|
||||||
|
expect(staticObjPluginFn).not.toHaveBeenCalled();
|
||||||
|
// @ts-ignore
|
||||||
|
OverlayScrollbars.staticObjPluginFn();
|
||||||
|
expect(staticObjPluginFn).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
const osInstance = OverlayScrollbars(div, {});
|
||||||
|
expect(instanceObjPluginFn).not.toHaveBeenCalled();
|
||||||
|
// @ts-ignore
|
||||||
|
osInstance.instanceObjPluginFn();
|
||||||
|
expect(instanceObjPluginFn).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,19 +9,24 @@ describe('plugins', () => {
|
|||||||
test('addPlugin single', () => {
|
test('addPlugin single', () => {
|
||||||
const myPlugin = {};
|
const myPlugin = {};
|
||||||
const myPlugin2 = {};
|
const myPlugin2 = {};
|
||||||
addPlugin({
|
const addedPlugins = addPlugin({
|
||||||
myPlugin,
|
myPlugin,
|
||||||
myPlugin2,
|
myPlugin2,
|
||||||
});
|
});
|
||||||
|
expect(addedPlugins.length).toBe(2);
|
||||||
|
expect(addedPlugins[0]).toBe(myPlugin);
|
||||||
|
expect(addedPlugins[1]).toBe(myPlugin2);
|
||||||
|
|
||||||
const plugins = getPlugins();
|
const plugins = getPlugins();
|
||||||
expect(plugins.myPlugin).toBe(myPlugin);
|
expect(plugins.myPlugin).toBe(myPlugin);
|
||||||
expect(plugins.myPlugin2).toBe(undefined); // one plugin per object
|
// multiple "sub-plugins" per plugin object possible to support "static", "instanceObj" and "staticObj" sub-plugins per plugin
|
||||||
|
expect(plugins.myPlugin2).toBe(myPlugin2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('addPlugin multiple', () => {
|
test('addPlugin multiple', () => {
|
||||||
const myPlugin = {};
|
const myPlugin = {};
|
||||||
const myPlugin2 = {};
|
const myPlugin2 = {};
|
||||||
addPlugin([
|
const addedPlugins = addPlugin([
|
||||||
{
|
{
|
||||||
myPlugin,
|
myPlugin,
|
||||||
},
|
},
|
||||||
@@ -29,6 +34,11 @@ describe('plugins', () => {
|
|||||||
myPlugin2,
|
myPlugin2,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
expect(addedPlugins.length).toBe(2);
|
||||||
|
expect(addedPlugins[0]).toBe(myPlugin);
|
||||||
|
expect(addedPlugins[1]).toBe(myPlugin2);
|
||||||
|
|
||||||
const plugins = getPlugins();
|
const plugins = getPlugins();
|
||||||
expect(plugins.myPlugin).toBe(myPlugin);
|
expect(plugins.myPlugin).toBe(myPlugin);
|
||||||
expect(plugins.myPlugin2).toBe(myPlugin2);
|
expect(plugins.myPlugin2).toBe(myPlugin2);
|
||||||
|
|||||||
+363
@@ -0,0 +1,363 @@
|
|||||||
|
import {
|
||||||
|
createScrollbarsSetupElements,
|
||||||
|
ScrollbarsSetupElement,
|
||||||
|
ScrollbarsSetupElementsObj,
|
||||||
|
ScrollbarStructure,
|
||||||
|
} from 'setups/scrollbarsSetup/scrollbarsSetup.elements';
|
||||||
|
import {
|
||||||
|
createStructureSetupElements,
|
||||||
|
StructureSetupElementsObj,
|
||||||
|
} from 'setups/structureSetup/structureSetup.elements';
|
||||||
|
import {
|
||||||
|
classNameScrollbar,
|
||||||
|
classNameScrollbarHorizontal,
|
||||||
|
classNameScrollbarVertical,
|
||||||
|
classNameScrollbarTrack,
|
||||||
|
classNameScrollbarHandle,
|
||||||
|
classNamesScrollbarTransitionless,
|
||||||
|
} from 'classnames';
|
||||||
|
import type { InitializationTarget } from 'initialization';
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
jest.mock('support/compatibility/apis', () => {
|
||||||
|
const originalModule = jest.requireActual('support/compatibility/apis');
|
||||||
|
return {
|
||||||
|
...originalModule,
|
||||||
|
// @ts-ignore
|
||||||
|
setT: jest.fn().mockImplementation((...args) => setTimeout(...args)),
|
||||||
|
clearT: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getTarget = () => document.body.firstElementChild as HTMLElement;
|
||||||
|
|
||||||
|
const domSnapshot = (element?: HTMLElement) => {
|
||||||
|
const getResult = () => (element ? element.innerHTML : document.documentElement.outerHTML);
|
||||||
|
return [getResult(), () => getResult()] as [string, () => string];
|
||||||
|
};
|
||||||
|
|
||||||
|
const createStructureSetupElementsProxy = (target: InitializationTarget) => {
|
||||||
|
const [structureElements, , destroyStructureElements] = createStructureSetupElements(target);
|
||||||
|
const [elements, appendElements, destroy] = createScrollbarsSetupElements(
|
||||||
|
target,
|
||||||
|
structureElements,
|
||||||
|
() => () => {}
|
||||||
|
);
|
||||||
|
|
||||||
|
appendElements();
|
||||||
|
|
||||||
|
return [
|
||||||
|
elements,
|
||||||
|
() => {
|
||||||
|
destroyStructureElements();
|
||||||
|
destroy();
|
||||||
|
},
|
||||||
|
structureElements,
|
||||||
|
] as [
|
||||||
|
elements: ScrollbarsSetupElementsObj,
|
||||||
|
destroy: () => void,
|
||||||
|
structureElements: StructureSetupElementsObj
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertCorrectDOMStructure = (
|
||||||
|
elements: ScrollbarsSetupElementsObj,
|
||||||
|
target: HTMLElement | ((isHorizontal?: boolean) => HTMLElement),
|
||||||
|
getTargetStructure?: (
|
||||||
|
structures: ScrollbarStructure[],
|
||||||
|
isHorizontal?: boolean
|
||||||
|
) => ScrollbarStructure
|
||||||
|
) => {
|
||||||
|
const getStructure = (isHorizontal?: boolean) =>
|
||||||
|
isHorizontal
|
||||||
|
? elements._horizontal._scrollbarStructures
|
||||||
|
: elements._vertical._scrollbarStructures;
|
||||||
|
const assertScrollbarStructure = (isHorizontal?: boolean) => {
|
||||||
|
const resolvedTarget = typeof target === 'function' ? target(isHorizontal) : target;
|
||||||
|
const domScrollbar = resolvedTarget.querySelector(
|
||||||
|
`.${isHorizontal ? classNameScrollbarHorizontal : classNameScrollbarVertical}`
|
||||||
|
) as HTMLElement;
|
||||||
|
const structures = getStructure(isHorizontal);
|
||||||
|
|
||||||
|
expect(structures.length).toBeGreaterThanOrEqual(1);
|
||||||
|
|
||||||
|
const targetStructure = getTargetStructure?.(structures, isHorizontal) || structures[0];
|
||||||
|
const isMainStructure = targetStructure === structures[0];
|
||||||
|
const { _scrollbar, _track, _handle } = targetStructure;
|
||||||
|
|
||||||
|
// classnames
|
||||||
|
expect(domScrollbar).toEqual(expect.any(HTMLElement));
|
||||||
|
expect(domScrollbar.classList.contains(classNameScrollbar)).toBe(true);
|
||||||
|
expect(_track.classList.contains(classNameScrollbarTrack)).toBe(true);
|
||||||
|
expect(_handle.classList.contains(classNameScrollbarHandle)).toBe(true);
|
||||||
|
expect(_track.classList.length).toBe(1);
|
||||||
|
expect(_handle.classList.length).toBe(1);
|
||||||
|
if (isMainStructure) {
|
||||||
|
expect(domScrollbar.classList.contains(classNamesScrollbarTransitionless)).toBe(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// structure
|
||||||
|
expect(_scrollbar).toBe(domScrollbar);
|
||||||
|
expect(_track.closest(`.${classNameScrollbar}`)).toBe(_scrollbar);
|
||||||
|
expect(_handle.closest(`.${classNameScrollbarTrack}`)).toBe(_track);
|
||||||
|
};
|
||||||
|
|
||||||
|
assertScrollbarStructure(true);
|
||||||
|
assertScrollbarStructure();
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('scrollbarsSetup.elements', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
document.body.innerHTML = '<div></div>';
|
||||||
|
});
|
||||||
|
|
||||||
|
[false, true].forEach((targetIsObj) => {
|
||||||
|
describe(`as target ${targetIsObj ? 'object' : 'element'}`, () => {
|
||||||
|
test('initialization and destruction', () => {
|
||||||
|
const target = getTarget();
|
||||||
|
const [beforeInitSnapshot, beforeInitSnapshotFn] = domSnapshot();
|
||||||
|
const [elements, destroy] = createStructureSetupElementsProxy(
|
||||||
|
targetIsObj ? { target } : target
|
||||||
|
);
|
||||||
|
|
||||||
|
assertCorrectDOMStructure(elements, target);
|
||||||
|
|
||||||
|
destroy();
|
||||||
|
|
||||||
|
expect(beforeInitSnapshot).toBe(beforeInitSnapshotFn());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cloning', () => {
|
||||||
|
const target = getTarget();
|
||||||
|
const [beforeInitSnapshot, beforeInitSnapshotFn] = domSnapshot();
|
||||||
|
const [elements, destroy] = createStructureSetupElementsProxy(
|
||||||
|
targetIsObj ? { target } : target
|
||||||
|
);
|
||||||
|
const clonedHorizontalSlot = document.createElement('div');
|
||||||
|
const clonedVerticalSlot = document.createElement('div');
|
||||||
|
|
||||||
|
const [beforeCloneHorizontalSnapshot, beforeCloneHorizontalSnapshotFn] =
|
||||||
|
domSnapshot(clonedHorizontalSlot);
|
||||||
|
const [beforeCloneVerticalSnapshot, beforeCloneVerticalSnapshotFn] =
|
||||||
|
domSnapshot(clonedVerticalSlot);
|
||||||
|
|
||||||
|
const clonedHorizontal = elements._horizontal._clone();
|
||||||
|
const clonedVertical = elements._vertical._clone();
|
||||||
|
|
||||||
|
clonedHorizontalSlot.append(clonedHorizontal._scrollbar);
|
||||||
|
clonedVerticalSlot.append(clonedVertical._scrollbar);
|
||||||
|
|
||||||
|
target.append(clonedHorizontalSlot);
|
||||||
|
target.append(clonedVerticalSlot);
|
||||||
|
|
||||||
|
assertCorrectDOMStructure(elements, target);
|
||||||
|
assertCorrectDOMStructure(
|
||||||
|
elements,
|
||||||
|
(isHorizontal) => (isHorizontal ? clonedHorizontalSlot : clonedVerticalSlot),
|
||||||
|
(structures, isHorizontal) => {
|
||||||
|
const clonedStructure = isHorizontal ? clonedHorizontal : clonedVertical;
|
||||||
|
return structures.find(
|
||||||
|
(structure) => structure._scrollbar === clonedStructure._scrollbar
|
||||||
|
)!;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
destroy();
|
||||||
|
|
||||||
|
// destroy cleans up clones as well
|
||||||
|
expect(beforeCloneHorizontalSnapshot).toBe(beforeCloneHorizontalSnapshotFn());
|
||||||
|
expect(beforeCloneVerticalSnapshot).toBe(beforeCloneVerticalSnapshotFn());
|
||||||
|
|
||||||
|
clonedHorizontalSlot.remove();
|
||||||
|
clonedVerticalSlot.remove();
|
||||||
|
|
||||||
|
expect(beforeInitSnapshot).toBe(beforeInitSnapshotFn());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addRemoveClass', () => {
|
||||||
|
test('add & remove classes to both axis', () => {
|
||||||
|
const target = getTarget();
|
||||||
|
const [elements] = createStructureSetupElementsProxy(target);
|
||||||
|
|
||||||
|
const horizontalStructures = elements._horizontal._scrollbarStructures;
|
||||||
|
const verticalStructures = elements._vertical._scrollbarStructures;
|
||||||
|
const className = 'aksjhdkasjd';
|
||||||
|
|
||||||
|
// clones before have the class
|
||||||
|
elements._horizontal._clone();
|
||||||
|
elements._vertical._clone();
|
||||||
|
|
||||||
|
elements._scrollbarsAddRemoveClass(className, true);
|
||||||
|
|
||||||
|
// clones after do not have the class
|
||||||
|
elements._horizontal._clone();
|
||||||
|
elements._vertical._clone();
|
||||||
|
|
||||||
|
horizontalStructures.forEach(({ _scrollbar }, i, arr) => {
|
||||||
|
if (i === arr.length - 1) {
|
||||||
|
expect(_scrollbar.classList.contains(className)).toBe(false);
|
||||||
|
} else {
|
||||||
|
expect(_scrollbar.classList.contains(className)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
verticalStructures.forEach(({ _scrollbar }, i, arr) => {
|
||||||
|
if (i === arr.length - 1) {
|
||||||
|
expect(_scrollbar.classList.contains(className)).toBe(false);
|
||||||
|
} else {
|
||||||
|
expect(_scrollbar.classList.contains(className)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
elements._scrollbarsAddRemoveClass(className);
|
||||||
|
|
||||||
|
horizontalStructures.forEach(({ _scrollbar }) => {
|
||||||
|
expect(_scrollbar.classList.contains(className)).toBe(false);
|
||||||
|
});
|
||||||
|
verticalStructures.forEach(({ _scrollbar }) => {
|
||||||
|
expect(_scrollbar.classList.contains(className)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('add & remove classes to individual axis', () => {
|
||||||
|
const target = getTarget();
|
||||||
|
const [elements] = createStructureSetupElementsProxy(target);
|
||||||
|
|
||||||
|
const horizontalStructures = elements._horizontal._scrollbarStructures;
|
||||||
|
const verticalStructures = elements._vertical._scrollbarStructures;
|
||||||
|
const classNameHorizontal = 'hhhhhhhhh12sdsdf';
|
||||||
|
const classNameVertical = 'vvvvvvv12sdsdf';
|
||||||
|
|
||||||
|
// clones before have the class
|
||||||
|
elements._horizontal._clone();
|
||||||
|
elements._vertical._clone();
|
||||||
|
|
||||||
|
elements._scrollbarsAddRemoveClass(classNameHorizontal, true, true);
|
||||||
|
elements._scrollbarsAddRemoveClass(classNameVertical, true, false);
|
||||||
|
|
||||||
|
// clones after do not have the class
|
||||||
|
elements._horizontal._clone();
|
||||||
|
elements._vertical._clone();
|
||||||
|
|
||||||
|
horizontalStructures.forEach(({ _scrollbar }, i, arr) => {
|
||||||
|
if (i === arr.length - 1) {
|
||||||
|
expect(_scrollbar.classList.contains(classNameHorizontal)).toBe(false);
|
||||||
|
expect(_scrollbar.classList.contains(classNameVertical)).toBe(false);
|
||||||
|
} else {
|
||||||
|
expect(_scrollbar.classList.contains(classNameHorizontal)).toBe(true);
|
||||||
|
expect(_scrollbar.classList.contains(classNameVertical)).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
verticalStructures.forEach(({ _scrollbar }, i, arr) => {
|
||||||
|
if (i === arr.length - 1) {
|
||||||
|
expect(_scrollbar.classList.contains(classNameHorizontal)).toBe(false);
|
||||||
|
expect(_scrollbar.classList.contains(classNameVertical)).toBe(false);
|
||||||
|
} else {
|
||||||
|
expect(_scrollbar.classList.contains(classNameHorizontal)).toBe(false);
|
||||||
|
expect(_scrollbar.classList.contains(classNameVertical)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
elements._scrollbarsAddRemoveClass(classNameHorizontal, false, true);
|
||||||
|
elements._scrollbarsAddRemoveClass(classNameVertical, false, false);
|
||||||
|
|
||||||
|
horizontalStructures.forEach(({ _scrollbar }) => {
|
||||||
|
expect(_scrollbar.classList.contains(classNameHorizontal)).toBe(false);
|
||||||
|
expect(_scrollbar.classList.contains(classNameVertical)).toBe(false);
|
||||||
|
});
|
||||||
|
verticalStructures.forEach(({ _scrollbar }) => {
|
||||||
|
expect(_scrollbar.classList.contains(classNameHorizontal)).toBe(false);
|
||||||
|
expect(_scrollbar.classList.contains(classNameVertical)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('initialization and destruction in custom slot', () => {
|
||||||
|
const target = getTarget();
|
||||||
|
const slotFn = jest.fn(() => document.body);
|
||||||
|
const [beforeInitSnapshot, beforeInitSnapshotFn] = domSnapshot();
|
||||||
|
const [elements, destroy, structureElements] = createStructureSetupElementsProxy({
|
||||||
|
target,
|
||||||
|
scrollbars: {
|
||||||
|
slot: slotFn,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(slotFn).toHaveBeenCalledTimes(1);
|
||||||
|
expect(slotFn).toHaveBeenCalledWith(
|
||||||
|
structureElements._target,
|
||||||
|
structureElements._host,
|
||||||
|
structureElements._viewport
|
||||||
|
);
|
||||||
|
|
||||||
|
assertCorrectDOMStructure(elements, document.body);
|
||||||
|
|
||||||
|
destroy();
|
||||||
|
|
||||||
|
expect(beforeInitSnapshot).toBe(beforeInitSnapshotFn());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handleStyle', () => {
|
||||||
|
const target = getTarget();
|
||||||
|
const [elements] = createStructureSetupElementsProxy(target);
|
||||||
|
const testHandleStyle = (scrollbarSetupElement: ScrollbarsSetupElement) => {
|
||||||
|
// before cloned elements have the style
|
||||||
|
scrollbarSetupElement._clone();
|
||||||
|
|
||||||
|
scrollbarSetupElement._handleStyle((structure) => {
|
||||||
|
const { _scrollbar } = structure;
|
||||||
|
return [_scrollbar, { width: '0px' }];
|
||||||
|
});
|
||||||
|
scrollbarSetupElement._handleStyle((structure) => {
|
||||||
|
const { _track } = structure;
|
||||||
|
return [_track, { width: '1px' }];
|
||||||
|
});
|
||||||
|
scrollbarSetupElement._handleStyle((structure) => {
|
||||||
|
const { _handle } = structure;
|
||||||
|
return [_handle, { width: '2px' }];
|
||||||
|
});
|
||||||
|
|
||||||
|
// before cloned elements don not have the style
|
||||||
|
scrollbarSetupElement._clone();
|
||||||
|
|
||||||
|
scrollbarSetupElement._scrollbarStructures.forEach(
|
||||||
|
({ _scrollbar, _track, _handle }, i, arr) => {
|
||||||
|
if (i === arr.length - 1) {
|
||||||
|
expect(_scrollbar.style.width).not.toBe('0px');
|
||||||
|
expect(_track.style.width).not.toBe('1px');
|
||||||
|
expect(_handle.style.width).not.toBe('2px');
|
||||||
|
} else {
|
||||||
|
expect(_scrollbar.style.width).toBe('0px');
|
||||||
|
expect(_track.style.width).toBe('1px');
|
||||||
|
expect(_handle.style.width).toBe('2px');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
testHandleStyle(elements._horizontal);
|
||||||
|
testHandleStyle(elements._vertical);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removes transitionless class', () => {
|
||||||
|
const target = getTarget();
|
||||||
|
const [elements] = createStructureSetupElementsProxy(target);
|
||||||
|
const testHasTransitionlessClass = (
|
||||||
|
setupElement: ScrollbarsSetupElement,
|
||||||
|
expected: boolean
|
||||||
|
) => {
|
||||||
|
const { _scrollbar } = setupElement._scrollbarStructures[0];
|
||||||
|
expect(_scrollbar.classList.contains(classNamesScrollbarTransitionless)).toBe(expected);
|
||||||
|
};
|
||||||
|
|
||||||
|
testHasTransitionlessClass(elements._horizontal, true);
|
||||||
|
testHasTransitionlessClass(elements._vertical, true);
|
||||||
|
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
testHasTransitionlessClass(elements._horizontal, false);
|
||||||
|
testHasTransitionlessClass(elements._vertical, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
+101
-43
@@ -1,6 +1,11 @@
|
|||||||
import { hasClass, is, isFunction, isHTMLElement } from 'support';
|
import { hasClass, is, isFunction, isHTMLElement } from 'support';
|
||||||
import { dataAttributeHost } from 'classnames';
|
import {
|
||||||
import { InternalEnvironment } from 'environment';
|
dataAttributeHost,
|
||||||
|
classNamePadding,
|
||||||
|
classNameViewport,
|
||||||
|
classNameContent,
|
||||||
|
} from 'classnames';
|
||||||
|
import { getEnvironment, InternalEnvironment } from 'environment';
|
||||||
import {
|
import {
|
||||||
createStructureSetupElements,
|
createStructureSetupElements,
|
||||||
StructureSetupElementsObj,
|
StructureSetupElementsObj,
|
||||||
@@ -12,9 +17,8 @@ import type {
|
|||||||
InitializationTargetObject,
|
InitializationTargetObject,
|
||||||
} from 'initialization';
|
} from 'initialization';
|
||||||
|
|
||||||
const mockGetEnvironment = jest.fn();
|
|
||||||
jest.mock('environment', () => ({
|
jest.mock('environment', () => ({
|
||||||
getEnvironment: jest.fn().mockImplementation(() => mockGetEnvironment()),
|
getEnvironment: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('support/compatibility/apis', () => {
|
jest.mock('support/compatibility/apis', () => {
|
||||||
@@ -81,10 +85,10 @@ const clearBody = () => {
|
|||||||
|
|
||||||
const getElements = (targetType: TargetType) => {
|
const getElements = (targetType: TargetType) => {
|
||||||
const target = getTarget(targetType);
|
const target = getTarget(targetType);
|
||||||
const host = document.querySelector('[data-overlayscrollbars]')!;
|
const host = document.querySelector(`[${dataAttributeHost}]`)!;
|
||||||
const padding = document.querySelector('.os-padding')!;
|
const padding = document.querySelector(`.${classNamePadding}`)!;
|
||||||
const viewport = document.querySelector('.os-viewport')!;
|
const viewport = document.querySelector(`.${classNameViewport}`)!;
|
||||||
const content = document.querySelector('.os-content')!;
|
const content = document.querySelector(`.${classNameContent}`)!;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
target,
|
target,
|
||||||
@@ -133,7 +137,9 @@ const assertCorrectDOMStructure = (targetType: TargetType, viewportIsTarget: boo
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createStructureSetupProxy = (target: InitializationTarget): StructureSetupElementsProxy => {
|
const createStructureSetupElementsProxy = (
|
||||||
|
target: InitializationTarget
|
||||||
|
): StructureSetupElementsProxy => {
|
||||||
const [elements, appendElements, destroy] = createStructureSetupElements(target);
|
const [elements, appendElements, destroy] = createStructureSetupElements(target);
|
||||||
appendElements();
|
appendElements();
|
||||||
return {
|
return {
|
||||||
@@ -422,9 +428,15 @@ const envInitStrategyViewportIsTarget = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('structureSetup', () => {
|
describe('structureSetup.elements', () => {
|
||||||
afterEach(() => clearBody());
|
afterEach(() => clearBody());
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(getEnvironment as jest.Mock).mockImplementation(() =>
|
||||||
|
jest.requireActual('environment').getEnvironment()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
[
|
[
|
||||||
envDefault,
|
envDefault,
|
||||||
envNativeScrollbarStyling,
|
envNativeScrollbarStyling,
|
||||||
@@ -436,8 +448,8 @@ describe('structureSetup', () => {
|
|||||||
].forEach((envWithName) => {
|
].forEach((envWithName) => {
|
||||||
const { env: currEnv, name } = envWithName;
|
const { env: currEnv, name } = envWithName;
|
||||||
describe(`Environment: ${name}`, () => {
|
describe(`Environment: ${name}`, () => {
|
||||||
beforeAll(() => {
|
beforeEach(() => {
|
||||||
mockGetEnvironment.mockImplementation(() => currEnv);
|
(getEnvironment as jest.Mock).mockImplementation(() => currEnv);
|
||||||
});
|
});
|
||||||
|
|
||||||
(['element', 'textarea', 'body'] as TargetType[]).forEach((targetType) => {
|
(['element', 'textarea', 'body'] as TargetType[]).forEach((targetType) => {
|
||||||
@@ -447,7 +459,7 @@ describe('structureSetup', () => {
|
|||||||
const snapshot = fillBody(targetType);
|
const snapshot = fillBody(targetType);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy(getTarget(targetType)),
|
createStructureSetupElementsProxy(getTarget(targetType)),
|
||||||
currEnv
|
currEnv
|
||||||
);
|
);
|
||||||
assertCorrectDOMStructure(targetType, elements._viewportIsTarget);
|
assertCorrectDOMStructure(targetType, elements._viewportIsTarget);
|
||||||
@@ -458,7 +470,7 @@ describe('structureSetup', () => {
|
|||||||
const snapshot = fillBody(targetType);
|
const snapshot = fillBody(targetType);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({ target: getTarget(targetType) }),
|
createStructureSetupElementsProxy({ target: getTarget(targetType) }),
|
||||||
currEnv
|
currEnv
|
||||||
);
|
);
|
||||||
assertCorrectDOMStructure(targetType, elements._viewportIsTarget);
|
assertCorrectDOMStructure(targetType, elements._viewportIsTarget);
|
||||||
@@ -476,7 +488,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -497,7 +509,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: () => document.querySelector<HTMLElement>('#host'),
|
host: () => document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -518,7 +530,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -541,7 +553,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -564,7 +576,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: () => document.querySelector<HTMLElement>('#host'),
|
host: () => document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -586,7 +598,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -608,7 +620,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -628,7 +640,7 @@ describe('structureSetup', () => {
|
|||||||
const snapshot = fillBody(targetType);
|
const snapshot = fillBody(targetType);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
padding: false,
|
padding: false,
|
||||||
@@ -644,7 +656,7 @@ describe('structureSetup', () => {
|
|||||||
const snapshot = fillBody(targetType);
|
const snapshot = fillBody(targetType);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
content: () => false,
|
content: () => false,
|
||||||
@@ -662,7 +674,7 @@ describe('structureSetup', () => {
|
|||||||
const snapshot = fillBody(targetType);
|
const snapshot = fillBody(targetType);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
padding: () => true,
|
padding: () => true,
|
||||||
@@ -678,7 +690,7 @@ describe('structureSetup', () => {
|
|||||||
const snapshot = fillBody(targetType);
|
const snapshot = fillBody(targetType);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
content: true,
|
content: true,
|
||||||
@@ -696,7 +708,7 @@ describe('structureSetup', () => {
|
|||||||
const snapshot = fillBody(targetType);
|
const snapshot = fillBody(targetType);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
padding: false,
|
padding: false,
|
||||||
@@ -715,7 +727,7 @@ describe('structureSetup', () => {
|
|||||||
const snapshot = fillBody(targetType);
|
const snapshot = fillBody(targetType);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
padding: true,
|
padding: true,
|
||||||
@@ -738,7 +750,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -761,7 +773,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -784,7 +796,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -807,7 +819,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -830,7 +842,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -852,7 +864,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -874,7 +886,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -896,7 +908,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -918,7 +930,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -941,7 +953,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -964,7 +976,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -986,7 +998,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: () => document.querySelector<HTMLElement>('#host'),
|
host: () => document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -1008,7 +1020,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -1030,7 +1042,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -1052,7 +1064,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: document.querySelector<HTMLElement>('#host'),
|
host: document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -1075,7 +1087,7 @@ describe('structureSetup', () => {
|
|||||||
);
|
);
|
||||||
const [elements, destroy] = assertCorrectSetupElements(
|
const [elements, destroy] = assertCorrectSetupElements(
|
||||||
targetType,
|
targetType,
|
||||||
createStructureSetupProxy({
|
createStructureSetupElementsProxy({
|
||||||
target: getTarget(targetType),
|
target: getTarget(targetType),
|
||||||
elements: {
|
elements: {
|
||||||
host: () => document.querySelector<HTMLElement>('#host'),
|
host: () => document.querySelector<HTMLElement>('#host'),
|
||||||
@@ -1095,4 +1107,50 @@ describe('structureSetup', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('focus', () => {
|
||||||
|
describe('shift tabindex to viewport', () => {
|
||||||
|
test('with pointerdown on body', () => {
|
||||||
|
const { elements } = createStructureSetupElementsProxy(document.body);
|
||||||
|
expect(elements._viewport.getAttribute('tabindex')).toBe('-1');
|
||||||
|
expect(document.activeElement).toBe(elements._viewport);
|
||||||
|
|
||||||
|
elements._documentElm.dispatchEvent(new Event('pointerdown'));
|
||||||
|
|
||||||
|
expect(elements._viewport.getAttribute('tabindex')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with keydown on element', () => {
|
||||||
|
document.body.innerHTML = '<div tabindex="123"></div>';
|
||||||
|
const target = document.body.firstElementChild as HTMLElement;
|
||||||
|
target.focus();
|
||||||
|
|
||||||
|
const { elements } = createStructureSetupElementsProxy(target);
|
||||||
|
expect(elements._viewport.getAttribute('tabindex')).toBe('-1');
|
||||||
|
expect(document.activeElement).toBe(elements._viewport);
|
||||||
|
|
||||||
|
elements._documentElm.dispatchEvent(new Event('keydown'));
|
||||||
|
|
||||||
|
expect(elements._viewport.getAttribute('tabindex')).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shift tabindex back to original activeElement', () => {
|
||||||
|
document.body.innerHTML = '<input type="text" value="hi"></input>';
|
||||||
|
const input = document.querySelector('input') as HTMLInputElement;
|
||||||
|
const target = document.body;
|
||||||
|
|
||||||
|
input.focus();
|
||||||
|
const preInitFocus = document.activeElement;
|
||||||
|
|
||||||
|
const { elements } = createStructureSetupElementsProxy(target);
|
||||||
|
|
||||||
|
expect(preInitFocus).toBe(document.activeElement);
|
||||||
|
|
||||||
|
elements._documentElm.dispatchEvent(new Event('pointerdown'));
|
||||||
|
elements._documentElm.dispatchEvent(new Event('keydown'));
|
||||||
|
|
||||||
|
expect(preInitFocus).toBe(document.activeElement);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user