From a20ae81f233bc355c0070e035df31a4e06d5e447 Mon Sep 17 00:00:00 2001 From: Rene Date: Fri, 11 Jun 2021 18:19:11 +0200 Subject: [PATCH] improve initialization strategy and begin with scrollbars --- package.json | 2 +- packages/overlayscrollbars/src/classnames.ts | 6 + packages/overlayscrollbars/src/environment.ts | 42 +- .../src/lifecycles/lifecycleHub.ts | 6 +- .../src/overlayscrollbars.ts | 13 +- .../src/setups/scrollbarsSetup.ts | 70 ++ .../src/setups/structureSetup.ts | 259 ++-- .../src/styles/overlayscrollbars.scss | 1 + .../src/styles/scrollbars.scss | 116 ++ .../src/support/utils/types.ts | 2 +- packages/overlayscrollbars/src/typings.ts | 43 +- .../tests/jsdom/setups/structureSetup.test.ts | 1109 +++++++++-------- yarn.lock | 8 +- 13 files changed, 1020 insertions(+), 657 deletions(-) create mode 100644 packages/overlayscrollbars/src/setups/scrollbarsSetup.ts create mode 100644 packages/overlayscrollbars/src/styles/scrollbars.scss diff --git a/package.json b/package.json index b271fc8..d0ec099 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "rollup-plugin-typescript2": "^0.27.1", "should": "^13.2.3", "tslib": "^2.2.0", - "typescript": "^4.2.4", + "typescript": "^4.3.2", "utf-8-validate": "^5.0.2" }, "scripts": { diff --git a/packages/overlayscrollbars/src/classnames.ts b/packages/overlayscrollbars/src/classnames.ts index 924774f..0f6b441 100644 --- a/packages/overlayscrollbars/src/classnames.ts +++ b/packages/overlayscrollbars/src/classnames.ts @@ -17,3 +17,9 @@ export const classNameSizeObserverListenerItem = `${classNameSizeObserverListene export const classNameSizeObserverListenerItemFinal = `${classNameSizeObserverListenerItem}-final`; export const classNameTrinsicObserver = 'os-trinsic-observer'; + +export const classNameScrollbar = 'os-scrollbar'; +export const classNameScrollbarHorizontal = `${classNameScrollbar}-horizontal`; +export const classNameScrollbarVertical = `${classNameScrollbar}-vertical`; +export const classNameScrollbarTrack = 'os-scrollbar-track'; +export const classNameScrollbarHandle = 'os-scrollbar-handle'; diff --git a/packages/overlayscrollbars/src/environment.ts b/packages/overlayscrollbars/src/environment.ts index db364c5..c3fe6a9 100644 --- a/packages/overlayscrollbars/src/environment.ts +++ b/packages/overlayscrollbars/src/environment.ts @@ -25,12 +25,40 @@ import { classNameViewportScrollbarStyling, } from 'classnames'; import { OSOptions, defaultOptions } from 'options'; +import { OSTargetElement } from 'typings'; -export interface InitializationStrategy { - _padding: boolean; - _content: boolean; +type StructureInitializationElementFn = ((target: OSTargetElement) => HTMLElement | T) | T; + +type ScrollbarsInitializationElementFn = ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => HTMLElement | T) | T; + +/** + * A Static element is an element which MUST be generated. + * If null (or the returned result is null), the initialization function is generatig the element, otherwise + * the element returned by the function acts as the generated element. + */ +export type StructureInitializationStaticElement = StructureInitializationElementFn; + +/** + * A Dynamic element is an element which CAN be generated. + * If null (or the returned result is null), then the default behavior is used. + * If boolean (or the returned result is boolean), the generation of the element is forced (or not). + * If the function returns and element, the element returned by the function acts as the generated element. + */ +export type StructureInitializationDynamicElement = StructureInitializationElementFn; + +export interface StructureInitializationStrategy { + _host: StructureInitializationStaticElement; + _viewport: StructureInitializationStaticElement; + _padding: StructureInitializationDynamicElement; + _content: StructureInitializationDynamicElement; } +export interface ScrollbarsInitializationStrategy { + _scrollbarsSlot: ScrollbarsInitializationElementFn; +} + +export interface InitializationStrategy extends StructureInitializationStrategy, ScrollbarsInitializationStrategy {} + export type OnEnvironmentChanged = (env: Environment) => void; export interface Environment { _nativeScrollbarSize: XY; @@ -132,9 +160,13 @@ const getWindowDPR = (): number => { return window.devicePixelRatio || dDPI / sDPI; }; +// init function decides for all values const getDefaultInitializationStrategy = (nativeScrollbarStyling: boolean): InitializationStrategy => ({ - _padding: !nativeScrollbarStyling, - _content: false, + _host: null, + _viewport: null, + _padding: null, + _content: null, + _scrollbarsSlot: null, }); const createEnvironment = (): Environment => { diff --git a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts index aa1527e..11e2cf0 100644 --- a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts +++ b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts @@ -7,6 +7,7 @@ import { createTrinsicLifecycle } from 'lifecycles/trinsicLifecycle'; import { createPaddingLifecycle } from 'lifecycles/paddingLifecycle'; import { createOverflowLifecycle } from 'lifecycles/overflowLifecycle'; import { StyleObject } from 'typings'; +import { ScrollbarsSetup } from 'setups/scrollbarsSetup'; export type LifecycleCheckOption = (path: string) => LifecycleOptionInfo; @@ -99,7 +100,7 @@ const lifecycleCommunicationFallback: LifecycleCommunication = { }, }; -export const createLifecycleHub = (options: OSOptions, structureSetup: StructureSetup): LifecycleHubInstance => { +export const createLifecycleHub = (options: OSOptions, structureSetup: StructureSetup, scrollbarsSetup: ScrollbarsSetup): LifecycleHubInstance => { let lifecycleCommunication = lifecycleCommunicationFallback; const { _viewport } = structureSetup._targetObj; const { @@ -201,6 +202,9 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure _destroy() { destroyObservers(); removeEnvironmentListener(envUpdateListener); + + structureSetup._destroy(); + scrollbarsSetup._destroy(); }, }; }; diff --git a/packages/overlayscrollbars/src/overlayscrollbars.ts b/packages/overlayscrollbars/src/overlayscrollbars.ts index 97442e8..85a65ca 100644 --- a/packages/overlayscrollbars/src/overlayscrollbars.ts +++ b/packages/overlayscrollbars/src/overlayscrollbars.ts @@ -1,12 +1,13 @@ -import { OSTarget, OSTargetObject } from 'typings'; +import { OSTarget, OSInitializationObject } from 'typings'; import { PartialOptions, validateOptions, assignDeep, isEmptyObject } from 'support'; import { createStructureSetup, StructureSetup } from 'setups/structureSetup'; +import { createScrollbarsSetup, ScrollbarsSetup } from 'setups/scrollbarsSetup'; import { createLifecycleHub } from 'lifecycles/lifecycleHub'; import { OSOptions, optionsTemplate } from 'options'; import { getEnvironment } from 'environment'; export interface OverlayScrollbarsStatic { - (target: OSTarget | OSTargetObject, options?: PartialOptions, extensions?: any): OverlayScrollbars; + (target: OSTarget | OSInitializationObject, options?: PartialOptions, extensions?: any): OverlayScrollbars; } export interface OverlayScrollbars { @@ -14,12 +15,13 @@ export interface OverlayScrollbars { options(newOptions?: PartialOptions): OSOptions; update(force?: boolean): void; + destroy(): void; state(): any; } export const OverlayScrollbars: OverlayScrollbarsStatic = ( - target: OSTarget | OSTargetObject, + target: OSTarget | OSInitializationObject, options?: PartialOptions, extensions?: any ): OverlayScrollbars => { @@ -30,7 +32,9 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = ( validateOptions(options || ({} as PartialOptions), optionsTemplate, null, true)._validated ); const structureSetup: StructureSetup = createStructureSetup(target); - const lifecycleHub = createLifecycleHub(currentOptions, structureSetup); + const scrollbarsSetup: ScrollbarsSetup = createScrollbarsSetup(target, structureSetup); + const lifecycleHub = createLifecycleHub(currentOptions, structureSetup, scrollbarsSetup); + const instance: OverlayScrollbars = { options(newOptions?: PartialOptions) { if (newOptions) { @@ -47,6 +51,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = ( update(force?: boolean) { lifecycleHub._update(null, force); }, + destroy: () => lifecycleHub._destroy(), }; instance.update(true); diff --git a/packages/overlayscrollbars/src/setups/scrollbarsSetup.ts b/packages/overlayscrollbars/src/setups/scrollbarsSetup.ts new file mode 100644 index 0000000..b237ea8 --- /dev/null +++ b/packages/overlayscrollbars/src/setups/scrollbarsSetup.ts @@ -0,0 +1,70 @@ +import { appendChildren, createDiv, removeElements, isFunction } from 'support'; +import { + classNameScrollbar, + classNameScrollbarHorizontal, + classNameScrollbarVertical, + classNameScrollbarTrack, + classNameScrollbarHandle, +} from 'classnames'; +import { getEnvironment, ScrollbarsInitializationStrategy } from 'environment'; +import { OSTarget, ScrollbarsInitialization } from 'typings'; +import { StructureSetup } from 'setups/structureSetup'; + +export interface ScrollbarStructure { + _scrollbar: HTMLElement; + _track: HTMLElement; + _handle: HTMLElement; +} + +export interface ScrollbarsSetup { + _horizontalScrollbarStructure: ScrollbarStructure; + _verticalScrollbarStructure: ScrollbarStructure; + _destroy: () => void; +} + +const generateScrollbarDOM = (scrollbarClassName: string): ScrollbarStructure => { + const scrollbar = createDiv(`${classNameScrollbar} ${scrollbarClassName}`); + const track = createDiv(classNameScrollbarTrack); + const handle = createDiv(classNameScrollbarHandle); + + appendChildren(scrollbar, track); + appendChildren(track, handle); + + return { + _scrollbar: scrollbar, + _track: track, + _handle: handle, + }; +}; + +export const createScrollbarsSetup = (target: OSTarget | ScrollbarsInitialization, structureSetup: StructureSetup): ScrollbarsSetup => { + const { _getInitializationStrategy } = getEnvironment(); + const { _scrollbarsSlot: environmentScrollbarSlot } = _getInitializationStrategy() as ScrollbarsInitializationStrategy; + const { _targetObj, _targetCtx } = structureSetup; + const { _target, _host, _viewport } = _targetObj; + const initializationScrollbarSlot = !_targetCtx._targetIsElm && (target as ScrollbarsInitialization).scrollbarsSlot; + const initializationScrollbarSlotResult = isFunction(initializationScrollbarSlot) + ? initializationScrollbarSlot(_target, _host, _viewport) + : initializationScrollbarSlot; + const evaluatedScrollbarSlot = + initializationScrollbarSlotResult || + (isFunction(environmentScrollbarSlot) ? environmentScrollbarSlot(_target, _host, _viewport) : environmentScrollbarSlot) || + _host; + + const horizontalScrollbarStructure = generateScrollbarDOM(classNameScrollbarHorizontal); + const verticalScrollbarStructure = generateScrollbarDOM(classNameScrollbarVertical); + + const { _scrollbar: horizontalScrollbar } = horizontalScrollbarStructure; + const { _scrollbar: verticalScrollbar } = verticalScrollbarStructure; + + appendChildren(evaluatedScrollbarSlot, horizontalScrollbar); + appendChildren(evaluatedScrollbarSlot, verticalScrollbar); + + return { + _horizontalScrollbarStructure: horizontalScrollbarStructure, + _verticalScrollbarStructure: verticalScrollbarStructure, + _destroy() { + removeElements([horizontalScrollbar, verticalScrollbar]); + }, + }; +}; diff --git a/packages/overlayscrollbars/src/setups/structureSetup.ts b/packages/overlayscrollbars/src/setups/structureSetup.ts index 78bde43..f920203 100644 --- a/packages/overlayscrollbars/src/setups/structureSetup.ts +++ b/packages/overlayscrollbars/src/setups/structureSetup.ts @@ -7,7 +7,7 @@ import { insertAfter, addClass, parent, - isUndefined, + indexOf, removeElements, removeClass, push, @@ -15,6 +15,8 @@ import { insertBefore, attr, isBoolean, + isFunction, + keys, } from 'support'; import { classNameHost, @@ -24,8 +26,13 @@ import { classNameContent, classNameViewportScrollbarStyling, } from 'classnames'; -import { getEnvironment } from 'environment'; -import { OSTarget, OSTargetObject, OSTargetElement } from 'typings'; +import { + getEnvironment, + StructureInitializationStaticElement, + StructureInitializationDynamicElement, + StructureInitializationStrategy, +} from 'environment'; +import { OSTarget, OSTargetElement, StructureInitialization } from 'typings'; export interface OSTargetContext { _isTextarea: boolean; @@ -34,15 +41,16 @@ export interface OSTargetContext { _bodyElm: HTMLBodyElement; _windowElm: Window; _documentElm: HTMLDocument; + _targetIsElm: boolean; } export interface PreparedOSTargetObject { _target: OSTargetElement; _host: HTMLElement; _viewport: HTMLElement; - _padding: HTMLElement | false | null; - _content: HTMLElement | false | null; - _viewportArrange: HTMLStyleElement | false | null; + _padding: HTMLElement | false; + _content: HTMLElement | false; + _viewportArrange: HTMLStyleElement | false; } export interface StructureSetup { @@ -51,60 +59,113 @@ export interface StructureSetup { _destroy: () => void; } +let contentArrangeCounter = 0; + const unwrap = (elm: HTMLElement | false | null | undefined) => { appendChildren(parent(elm), contents(elm)); removeElements(elm); }; -let contentArrangeCounter = 0; -const createUniqueViewportArrangeElement = (): HTMLStyleElement => { - const elm = document.createElement('style'); +const createUniqueViewportArrangeElement = (): HTMLStyleElement | false => { + const { _nativeScrollbarStyling, _nativeScrollbarIsOverlaid, _cssCustomProperties } = getEnvironment(); + /* istanbul ignore next */ + const create = !_cssCustomProperties && !_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y); + const result = create ? document.createElement('style') : false; - attr(elm, 'id', `${classNameViewportArrange}-${contentArrangeCounter}`); - contentArrangeCounter++; - - return elm; -}; -const evaluateCreationFromStrategy = (initializationValue: HTMLElement | boolean | undefined, strategy: boolean): HTMLElement | false | undefined => { - const isBooleanValue = isBoolean(initializationValue); - if (isBooleanValue || isUndefined(initializationValue)) { - return (isBooleanValue ? initializationValue : strategy) && undefined; + if (result) { + attr(result, 'id', `${classNameViewportArrange}-${contentArrangeCounter}`); + contentArrangeCounter++; } - return initializationValue as HTMLElement; + + return result; }; -export const createStructureSetup = (target: OSTarget | OSTargetObject): StructureSetup => { - const { _getInitializationStrategy, _nativeScrollbarStyling, _nativeScrollbarIsOverlaid, _cssCustomProperties } = getEnvironment(); - const { _padding: paddingNeeded, _content: contentNeeded } = _getInitializationStrategy(); +const staticCreationFromStrategy = ( + target: OSTargetElement, + initializationValue: HTMLElement | undefined, + strategy: StructureInitializationStaticElement, + elementClass: string +): HTMLElement => { + const result = initializationValue ? initializationValue : isFunction(strategy) ? strategy(target) : (strategy as null); + return result ? result : createDiv(elementClass); +}; + +const dynamicCreationFromStrategy = ( + target: OSTargetElement, + initializationValue: HTMLElement | boolean | undefined, + strategy: StructureInitializationDynamicElement, + elementClass: string, + defaultValue: boolean +): HTMLElement | false => { + const takeInitializationValue = isBoolean(initializationValue) || initializationValue; + const result = takeInitializationValue ? (initializationValue as boolean | HTMLElement) : isFunction(strategy) ? strategy(target) : strategy; + + if (result === null) { + return defaultValue ? createDiv(elementClass) : false; + } + + return result === true ? createDiv(elementClass) : result; +}; + +export const createStructureSetup = (target: OSTarget | StructureInitialization): StructureSetup => { + const { _getInitializationStrategy, _nativeScrollbarStyling } = getEnvironment(); + const { + _host: hostInitializationStrategy, + _viewport: viewportInitializationStrategy, + _padding: paddingInitializationStrategy, + _content: contentInitializationStrategy, + } = _getInitializationStrategy() as StructureInitializationStrategy; const targetIsElm = isHTMLElement(target); - const osTargetObj: Partial = targetIsElm - ? ({} as Partial) - : { - _host: (target as OSTargetObject).host, - _target: (target as OSTargetObject).target, - _viewport: (target as OSTargetObject).viewport, - _padding: evaluateCreationFromStrategy((target as OSTargetObject).padding, paddingNeeded), - _content: evaluateCreationFromStrategy((target as OSTargetObject).content, contentNeeded), - }; - - if (targetIsElm) { - const viewport = createDiv(classNameViewport); - const padding = paddingNeeded && createDiv(classNamePadding); - const content = contentNeeded && createDiv(classNameContent); - - osTargetObj._target = target as OSTargetElement; - osTargetObj._padding = padding; - osTargetObj._viewport = viewport; - osTargetObj._content = content; - } - - let { _target, _padding, _viewport, _content } = osTargetObj; + const targetStructureInitialization = target as StructureInitialization; + const targetElement = targetIsElm ? (target as OSTargetElement) : targetStructureInitialization.target; + const isTextarea = is(targetElement, 'textarea'); + const isBody = !isTextarea && is(targetElement, 'body'); + const ownerDocument: HTMLDocument = targetElement!.ownerDocument; + const bodyElm = ownerDocument.body as HTMLBodyElement; + const wnd = ownerDocument.defaultView as Window; + const evaluatedTargetObj: PreparedOSTargetObject = { + _target: targetElement, + _host: isTextarea + ? staticCreationFromStrategy(targetElement, targetStructureInitialization.host, hostInitializationStrategy, classNameHost) + : (targetElement as HTMLElement), + _viewport: staticCreationFromStrategy(targetElement, targetStructureInitialization.viewport, viewportInitializationStrategy, classNameViewport), + _padding: dynamicCreationFromStrategy( + targetElement, + targetStructureInitialization.padding, + paddingInitializationStrategy, + classNamePadding, + !_nativeScrollbarStyling // default value for padding + ), + _content: dynamicCreationFromStrategy( + targetElement, + targetStructureInitialization.content, + contentInitializationStrategy, + classNameContent, + false // default value for content + ), + _viewportArrange: createUniqueViewportArrangeElement(), + }; + const ctx: OSTargetContext = { + _windowElm: wnd, + _documentElm: ownerDocument, + _htmlElm: parent(bodyElm) as HTMLHtmlElement, + _bodyElm: bodyElm, + _isTextarea: isTextarea, + _isBody: isBody, + _targetIsElm: targetIsElm, + }; + const generatedElements = keys(evaluatedTargetObj).reduce((arr, key: string) => { + const value = evaluatedTargetObj[key]; + return push(arr, value && !parent(value) ? value : false); + }, [] as HTMLElement[]); + const elementIsGenerated = (elm: HTMLElement | false) => (elm ? indexOf(generatedElements, elm) > -1 : null); + const { _target, _host, _padding, _viewport, _content, _viewportArrange } = evaluatedTargetObj; const destroyFns: (() => any)[] = []; - const isTextarea = is(_target, 'textarea'); - const isBody = !isTextarea && is(_target, 'body'); - const _host = (isTextarea ? osTargetObj._host || createDiv() : _target) as HTMLElement; - const getTargetContents = (contentSlot: HTMLElement) => (isTextarea ? (_target as HTMLTextAreaElement) : contents(contentSlot as HTMLElement)); - const isTextareaHostGenerated = isTextarea && _host !== osTargetObj._host; + const isTextareaHostGenerated = isTextarea && elementIsGenerated(_host); + const targetContents = isTextarea + ? _target + : contents([_content, _viewport, _padding, _host, _target].find((elm) => elementIsGenerated(elm) === false)); + const contentSlot = _content || _viewport; // only insert host for textarea after target if it was generated if (isTextareaHostGenerated) { @@ -116,88 +177,48 @@ export const createStructureSetup = (target: OSTarget | OSTargetObject): Structu }); } - if (targetIsElm) { - const contentSlot = _content || _viewport; - appendChildren(contentSlot, getTargetContents(_target!)); - appendChildren(_host, _padding); - appendChildren(_padding || _host, _viewport); - appendChildren(_viewport, _content); - - push(destroyFns, () => { - appendChildren(_host, contents(contentSlot)); - removeElements(_padding || _viewport); - removeClass(_host, classNameHost); - }); - } else { - const contentContainingElm = _content || _viewport || _padding || _host; - const createPadding = isUndefined(_padding); - const createViewport = isUndefined(_viewport); - const createContent = isUndefined(_content); - const targetContents = getTargetContents(contentContainingElm); - - _padding = osTargetObj._padding = createPadding ? createDiv() : _padding; - _viewport = osTargetObj._viewport = createViewport ? createDiv() : _viewport; - _content = osTargetObj._content = createContent ? createDiv() : _content; - - appendChildren(_host, _padding); - appendChildren(_padding || _host, _viewport); - appendChildren(_viewport, _content); - - const contentSlot = _content || _viewport; - appendChildren(contentSlot, targetContents); - - push(destroyFns, () => { - if (createContent) { - unwrap(_content); - } - if (createViewport) { - unwrap(_viewport); - } - if (createPadding) { - unwrap(_padding); - } - removeClass(_host, classNameHost); - removeClass(_padding, classNamePadding); - removeClass(_viewport, classNameViewport); - removeClass(_content, classNameContent); - }); - } + appendChildren(contentSlot, targetContents); + appendChildren(_host, _padding); + appendChildren(_padding || _host, _viewport); + appendChildren(_viewport, _content); addClass(_host, classNameHost); addClass(_padding, classNamePadding); addClass(_viewport, classNameViewport); addClass(_content, classNameContent); - const ownerDocument: HTMLDocument = _target!.ownerDocument; - const bodyElm = ownerDocument.body as HTMLBodyElement; - const wnd = ownerDocument.defaultView as Window; - const ctx: OSTargetContext = { - _windowElm: wnd, - _documentElm: ownerDocument, - _htmlElm: parent(bodyElm) as HTMLHtmlElement, - _bodyElm: bodyElm, - _isTextarea: isTextarea, - _isBody: isBody, - }; - // @ts-ignore - const obj: PreparedOSTargetObject = { - ...osTargetObj, - _host, - }; + push(destroyFns, () => { + if (targetIsElm) { + appendChildren(_host, contents(contentSlot)); + removeElements(_padding || _viewport); + removeClass(_host, classNameHost); + } else { + if (elementIsGenerated(_content)) { + unwrap(_content); + } + if (elementIsGenerated(_viewport)) { + unwrap(_viewport); + } + if (elementIsGenerated(_padding)) { + unwrap(_padding); + } + removeClass(_host, classNameHost); + removeClass(_padding, classNamePadding); + removeClass(_viewport, classNameViewport); + removeClass(_content, classNameContent); + } + }); if (_nativeScrollbarStyling) { push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling)); - } else if (!_cssCustomProperties && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y)) { - const viewportArrangeElm = createUniqueViewportArrangeElement(); - - insertBefore(_viewport, viewportArrangeElm); - push(destroyFns, removeElements.bind(0, viewportArrangeElm)); - - obj._viewportArrange = viewportArrangeElm; + } + if (_viewportArrange) { + insertBefore(_viewport, _viewportArrange); + push(destroyFns, removeElements.bind(0, _viewportArrange)); } return { - _targetObj: obj, + _targetObj: evaluatedTargetObj, _targetCtx: ctx, _destroy: () => { runEach(destroyFns); diff --git a/packages/overlayscrollbars/src/styles/overlayscrollbars.scss b/packages/overlayscrollbars/src/styles/overlayscrollbars.scss index 15616fc..5732a8a 100644 --- a/packages/overlayscrollbars/src/styles/overlayscrollbars.scss +++ b/packages/overlayscrollbars/src/styles/overlayscrollbars.scss @@ -1,5 +1,6 @@ @import './sizeobserver.scss'; @import './trinsicobserver.scss'; +@import './scrollbars.scss'; .os-environment { --os-custom-prop: -1; diff --git a/packages/overlayscrollbars/src/styles/scrollbars.scss b/packages/overlayscrollbars/src/styles/scrollbars.scss new file mode 100644 index 0000000..15059f1 --- /dev/null +++ b/packages/overlayscrollbars/src/styles/scrollbars.scss @@ -0,0 +1,116 @@ +.os-host-transition > .os-scrollbar, +.os-host-transition > .os-scrollbar-corner { + transition: opacity 0.3s, visibility 0.3s, top 0.3s, right 0.3s, bottom 0.3s, left 0.3s; +} +.os-scrollbar, +.os-scrollbar-corner { + position: absolute; + opacity: 1; + z-index: 1; +} +.os-scrollbar-corner { + bottom: 0; + right: 0; +} +.os-scrollbar { + pointer-events: none; +} +.os-scrollbar-track { + pointer-events: auto; + position: relative; + height: 100%; + width: 100%; + padding: 0 !important; + border: none !important; +} +.os-scrollbar-handle { + pointer-events: auto; + position: absolute; + width: 100%; + height: 100%; +} +.os-scrollbar-handle-off, +.os-scrollbar-track-off { + pointer-events: none; +} +.os-scrollbar.os-scrollbar-unusable, +.os-scrollbar.os-scrollbar-unusable * { + pointer-events: none !important; +} +.os-scrollbar.os-scrollbar-unusable .os-scrollbar-handle { + opacity: 0 !important; +} +.os-scrollbar-horizontal { + bottom: 0; + left: 0; +} +.os-scrollbar-vertical { + top: 0; + right: 0; +} +.os-host-rtl > .os-scrollbar-horizontal { + right: 0; +} +.os-host-rtl > .os-scrollbar-vertical { + right: auto; + left: 0; +} +.os-host-rtl > .os-scrollbar-corner { + right: auto; + left: 0; +} +.os-scrollbar-auto-hidden, +.os-padding + .os-scrollbar-corner, +.os-host-resize-disabled.os-host-scrollbar-horizontal-hidden > .os-scrollbar-corner, +.os-host-scrollbar-horizontal-hidden > .os-scrollbar-horizontal, +.os-host-resize-disabled.os-host-scrollbar-vertical-hidden > .os-scrollbar-corner, +.os-host-scrollbar-vertical-hidden > .os-scrollbar-vertical, +.os-scrollbar-horizontal.os-scrollbar-auto-hidden + .os-scrollbar-vertical + .os-scrollbar-corner, +.os-scrollbar-horizontal + .os-scrollbar-vertical.os-scrollbar-auto-hidden + .os-scrollbar-corner, +.os-scrollbar-horizontal.os-scrollbar-auto-hidden + .os-scrollbar-vertical.os-scrollbar-auto-hidden + .os-scrollbar-corner { + opacity: 0; + visibility: hidden; + pointer-events: none; +} +.os-scrollbar-corner-resize-both { + cursor: nwse-resize; +} +.os-host-rtl > .os-scrollbar-corner-resize-both { + cursor: nesw-resize; +} +.os-scrollbar-corner-resize-horizontal { + cursor: ew-resize; +} +.os-scrollbar-corner-resize-vertical { + cursor: ns-resize; +} +.os-dragging .os-scrollbar-corner.os-scrollbar-corner-resize { + cursor: default; +} +.os-host-resize-disabled.os-host-scrollbar-horizontal-hidden > .os-scrollbar-vertical { + top: 0; + bottom: 0; +} +.os-host-resize-disabled.os-host-scrollbar-vertical-hidden > .os-scrollbar-horizontal, +.os-host-rtl.os-host-resize-disabled.os-host-scrollbar-vertical-hidden > .os-scrollbar-horizontal { + right: 0; + left: 0; +} +.os-scrollbar:hover, +.os-scrollbar-corner.os-scrollbar-corner-resize { + opacity: 1 !important; + visibility: visible !important; +} +.os-scrollbar-corner.os-scrollbar-corner-resize { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgICB3aWR0aD0iMTAiICAgaGVpZ2h0PSIxMCIgICB2ZXJzaW9uPSIxLjEiPiAgPGcgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsLTEwNDIuMzYyMikiICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmUiPiAgICA8cGF0aCAgICAgICBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjQ5NDExNzY1O2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTpub25lIiAgICAgICBkPSJtIDcuNDI0MjE4NywxMDQyLjM2MjIgYyAtMC43MjM1NzkyLDAgLTEuMzEwMTU2MiwwLjU4NjYgLTEuMzEwMTU2MiwxLjMxMDIgMCwwLjI5OSAwLjEwNDM0MTksMC41NzEgMC4yNzI5NDkyLDAuNzkxNSAwLjIwOTEwMjQsMC4xNDEzIDAuNDY1NjIwNiwwLjIxODQgMC43MzY5NjI5LDAuMjE4NCAwLjcyMzU3OTMsMCAxLjMxMDE1NjMsLTAuNTg2NiAxLjMxMDE1NjMsLTEuMzEwMiAwLC0wLjI3MTMgLTAuMDc3MDkzLC0wLjUyNzggLTAuMjE4MzU5NCwtMC43MzcgLTAuMjIwNDk0MSwtMC4xNjg2IC0wLjQ5MjU0NDMsLTAuMjcyOSAtMC43OTE1NTI4LC0wLjI3MjkgeiBtIDAsMy4wODQzIGMgLTAuNzIzNTc5MiwwIC0xLjMxMDE1NjIsMC41ODY2IC0xLjMxMDE1NjIsMS4zMTAyIDAsMC4yOTkgMC4xMDQzNDE5LDAuNTcxIDAuMjcyOTQ5MiwwLjc5MTUgMC4yMDkxMDI0LDAuMTQxMyAwLjQ2NTYyMDYsMC4yMTg0IDAuNzM2OTYyOSwwLjIxODQgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjYgMS4zMTAxNTYzLC0xLjMxMDIgMCwtMC4yNzEzIC0wLjA3NzA5MywtMC41Mjc4IC0wLjIxODM1OTQsLTAuNzM2OSAtMC4yMjA0OTQxLC0wLjE2ODYgLTAuNDkyNTQ0MywtMC4yNzMgLTAuNzkxNTUyOCwtMC4yNzMgeiBtIC0zLjA4NDMyNjEsMCBjIC0wLjcyMzU3OTMsMCAtMS4zMTAxNTYzLDAuNTg2NiAtMS4zMTAxNTYzLDEuMzEwMiAwLDAuMjk5IDAuMTA0MzQxOSwwLjU3MSAwLjI3Mjk0OTIsMC43OTE1IDAuMjA5MTAyNCwwLjE0MTMgMC40NjU2MjA3LDAuMjE4NCAwLjczNjk2MjksMC4yMTg0IDAuNzIzNTc5MywwIDEuMzEwMTU2MywtMC41ODY2IDEuMzEwMTU2MywtMS4zMTAyIDAsLTAuMjcxMyAtMC4wNzcwOTMsLTAuNTI3OCAtMC4yMTgzNTk0LC0wLjczNjkgLTAuMjIwNDk0LC0wLjE2ODYgLTAuNDkyNTQ0MiwtMC4yNzMgLTAuNzkxNTUyNywtMC4yNzMgeiBtIC0zLjAyOTczNjQsMy4wMjk4IEMgMC41ODY1NzY5MywxMDQ4LjQ3NjMgMCwxMDQ5LjA2MjggMCwxMDQ5Ljc4NjQgYyAwLDAuMjk5IDAuMTA0MzQxOSwwLjU3MTEgMC4yNzI5NDkyMiwwLjc5MTYgMC4yMDkxMDIyOSwwLjE0MTIgMC40NjU2MjA2NSwwLjIxODMgMC43MzY5NjI4OCwwLjIxODMgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjUgMS4zMTAxNTYzLC0xLjMxMDEgMCwtMC4yNzE0IC0wLjA3NzA5MywtMC41Mjc5IC0wLjIxODM1OTQsLTAuNzM3IC0wLjIyMDQ5NDEsLTAuMTY4NiAtMC40OTI1NDQzLC0wLjI3MjkgLTAuNzkxNTUyOCwtMC4yNzI5IHogbSAzLjAyOTczNjQsMCBjIC0wLjcyMzU3OTMsMCAtMS4zMTAxNTYzLDAuNTg2NSAtMS4zMTAxNTYzLDEuMzEwMSAwLDAuMjk5IDAuMTA0MzQxOSwwLjU3MTEgMC4yNzI5NDkyLDAuNzkxNiAwLjIwOTEwMjQsMC4xNDEyIDAuNDY1NjIwNywwLjIxODMgMC43MzY5NjI5LDAuMjE4MyAwLjcyMzU3OTMsMCAxLjMxMDE1NjMsLTAuNTg2NSAxLjMxMDE1NjMsLTEuMzEwMSAwLC0wLjI3MTQgLTAuMDc3MDkzLC0wLjUyNzkgLTAuMjE4MzU5NCwtMC43MzcgLTAuMjIwNDk0LC0wLjE2ODYgLTAuNDkyNTQ0MiwtMC4yNzI5IC0wLjc5MTU1MjcsLTAuMjcyOSB6IG0gMy4wODQzMjYxLDAgYyAtMC43MjM1NzkyLDAgLTEuMzEwMTU2MiwwLjU4NjUgLTEuMzEwMTU2MiwxLjMxMDEgMCwwLjI5OSAwLjEwNDM0MTksMC41NzExIDAuMjcyOTQ5MiwwLjc5MTYgMC4yMDkxMDI0LDAuMTQxMiAwLjQ2NTYyMDYsMC4yMTgzIDAuNzM2OTYyOSwwLjIxODMgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjUgMS4zMTAxNTYzLC0xLjMxMDEgMCwtMC4yNzE0IC0wLjA3NzA5MywtMC41Mjc5IC0wLjIxODM1OTQsLTAuNzM3IC0wLjIyMDQ5NDEsLTAuMTY4NiAtMC40OTI1NDQzLC0wLjI3MjkgLTAuNzkxNTUyOCwtMC4yNzI5IHoiLz4gIDwvZz4gIDxnICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmUiPiAgICA8cGF0aCAgICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTpub25lIiAgICAgICBkPSJtIDguMjE1NzcxNSwwLjI3Mjk0OTIyIGMgMC4xNDEyNjY3LDAuMjA5MTAyMjkgMC4yMTgzNTk0LDAuNDY1NjIwNjUgMC4yMTgzNTk0LDAuNzM2OTYyODggMCwwLjcyMzU3OTMgLTAuNTg2NTc3LDEuMzEwMTU2MyAtMS4zMTAxNTYzLDEuMzEwMTU2MyAtMC4yNzEzNDIzLDAgLTAuNTI3ODYwNSwtMC4wNzcwOTMgLTAuNzM2OTYyOSwtMC4yMTgzNTk0IDAuMjM5NDEwNCwwLjMxMzA4NTkgMC42MTI2MzYyLDAuNTE4NjAzNSAxLjAzNzIwNywwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjU3NyAxLjMxMDE1NjMsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDc2IC0wLjIwNTUxNzYsLTAuNzk3Nzk2NTkgLTAuNTE4NjAzNSwtMS4wMzcyMDY5OCB6IG0gMCwzLjA4NDMyNjE4IGMgMC4xNDEyNjY3LDAuMjA5MTAyMyAwLjIxODM1OTQsMC40NjU2MjA2IDAuMjE4MzU5NCwwLjczNjk2MjkgMCwwLjcyMzU3OTMgLTAuNTg2NTc3LDEuMzEwMTU2MiAtMS4zMTAxNTYzLDEuMzEwMTU2MiAtMC4yNzEzNDIzLDAgLTAuNTI3ODYwNSwtMC4wNzcwOTMgLTAuNzM2OTYyOSwtMC4yMTgzNTkzIDAuMjM5NDEwNCwwLjMxMzA4NTkgMC42MTI2MzYyLDAuNTE4NjAzNSAxLjAzNzIwNywwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjU3NyAxLjMxMDE1NjMsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDggLTAuMjA1NTE3NiwtMC43OTc3OTY3IC0wLjUxODYwMzUsLTEuMDM3MjA3IHogbSAtMy4wODQzMjYyLDAgYyAwLjE0MTI2NjcsMC4yMDkxMDIzIDAuMjE4MzU5NCwwLjQ2NTYyMDYgMC4yMTgzNTk0LDAuNzM2OTYyOSAwLDAuNzIzNTc5MyAtMC41ODY1NzcsMS4zMTAxNTYyIC0xLjMxMDE1NjMsMS4zMTAxNTYyIC0wLjI3MTM0MjIsMCAtMC41Mjc4NjA1LC0wLjA3NzA5MyAtMC43MzY5NjI5LC0wLjIxODM1OTMgMC4yMzk0MTA0LDAuMzEzMDg1OSAwLjYxMjYzNjMsMC41MTg2MDM1IDEuMDM3MjA3MSwwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYyLC0wLjU4NjU3NyAxLjMxMDE1NjIsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDggLTAuMjA1NTE3NSwtMC43OTc3OTY3IC0wLjUxODYwMzUsLTEuMDM3MjA3IHogTSAyLjEwMTcwOSw2LjM4NzAxMTcgYyAwLjE0MTI2NjcsMC4yMDkxMDI0IDAuMjE4MzU5NCwwLjQ2NTYyMDYgMC4yMTgzNTk0LDAuNzM2OTYyOSAwLDAuNzIzNTc5MyAtMC41ODY1NzcsMS4zMTAxNTYzIC0xLjMxMDE1NjMsMS4zMTAxNTYzIC0wLjI3MTM0MjIzLDAgLTAuNTI3ODYwNTksLTAuMDc3MDkzIC0wLjczNjk2Mjg4LC0wLjIxODM1OTQgMC4yMzk0MTAzOSwwLjMxMzA4NTkgMC42MTI2MzYyMiwwLjUxODYwMzUgMS4wMzcyMDY5OCwwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjU3NyAxLjMxMDE1NjMsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDggLTAuMjA1NTE3NiwtMC43OTc3OTY2IC0wLjUxODYwMzUsLTEuMDM3MjA3IHogbSAzLjAyOTczNjMsMCBjIDAuMTQxMjY2NywwLjIwOTEwMjQgMC4yMTgzNTk0LDAuNDY1NjIwNiAwLjIxODM1OTQsMC43MzY5NjI5IDAsMC43MjM1NzkzIC0wLjU4NjU3NywxLjMxMDE1NjMgLTEuMzEwMTU2MywxLjMxMDE1NjMgLTAuMjcxMzQyMiwwIC0wLjUyNzg2MDUsLTAuMDc3MDkzIC0wLjczNjk2MjksLTAuMjE4MzU5NCAwLjIzOTQxMDQsMC4zMTMwODU5IDAuNjEyNjM2MywwLjUxODYwMzUgMS4wMzcyMDcxLDAuNTE4NjAzNSAwLjcyMzU3OTMsMCAxLjMxMDE1NjIsLTAuNTg2NTc3IDEuMzEwMTU2MiwtMS4zMTAxNTYzIDAsLTAuNDI0NTcwOCAtMC4yMDU1MTc1LC0wLjc5Nzc5NjYgLTAuNTE4NjAzNSwtMS4wMzcyMDcgeiBtIDMuMDg0MzI2MiwwIGMgMC4xNDEyNjY3LDAuMjA5MTAyNCAwLjIxODM1OTQsMC40NjU2MjA2IDAuMjE4MzU5NCwwLjczNjk2MjkgMCwwLjcyMzU3OTMgLTAuNTg2NTc3LDEuMzEwMTU2MyAtMS4zMTAxNTYzLDEuMzEwMTU2MyAtMC4yNzEzNDIzLDAgLTAuNTI3ODYwNSwtMC4wNzcwOTMgLTAuNzM2OTYyOSwtMC4yMTgzNTk0IDAuMjM5NDEwNCwwLjMxMzA4NTkgMC42MTI2MzYyLDAuNTE4NjAzNSAxLjAzNzIwNywwLjUxODYwMzUgMC43MjM1NzkzLDAgMS4zMTAxNTYzLC0wLjU4NjU3NyAxLjMxMDE1NjMsLTEuMzEwMTU2MyAwLC0wLjQyNDU3MDggLTAuMjA1NTE3NiwtMC43OTc3OTY2IC0wLjUxODYwMzUsLTEuMDM3MjA3IHoiIC8+ICA8L2c+PC9zdmc+); + background-repeat: no-repeat; + background-position: 100% 100%; + pointer-events: auto !important; +} +.os-host-rtl > .os-scrollbar-corner.os-scrollbar-corner-resize { + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.os-host-overflow { + overflow: hidden !important; +} diff --git a/packages/overlayscrollbars/src/support/utils/types.ts b/packages/overlayscrollbars/src/support/utils/types.ts index 71bb14c..445d60c 100644 --- a/packages/overlayscrollbars/src/support/utils/types.ts +++ b/packages/overlayscrollbars/src/support/utils/types.ts @@ -32,7 +32,7 @@ export function isBoolean(obj: any): obj is boolean { return typeof obj === 'boolean'; } -export function isFunction(obj: any): obj is (...args: Array) => unknown { +export function isFunction(obj: any): obj is (...args: any[]) => any { return typeof obj === 'function'; } diff --git a/packages/overlayscrollbars/src/typings.ts b/packages/overlayscrollbars/src/typings.ts index 0a8e280..545653c 100644 --- a/packages/overlayscrollbars/src/typings.ts +++ b/packages/overlayscrollbars/src/typings.ts @@ -1,3 +1,5 @@ + + export type PlainObject = { [name: string]: T }; export type StyleObject = { @@ -10,6 +12,19 @@ export type InternalVersionOf = { export type OSTargetElement = HTMLElement | HTMLTextAreaElement; +/** + * Static elements MUST be present. + */ +type StructureInitializationStaticElement = HTMLElement; + +/** + * Dynamic element CAN be present. + * If its a element the element will be handled as the repsective element. + * True means that the respective dynamic element is forced to be generated. + * False means that the respective dynamic element is forced NOT to be generated. + */ +type StructureInitializationDynamicElement = HTMLElement | boolean; + /** * Object for special initialization. * @@ -18,19 +33,29 @@ export type OSTargetElement = HTMLElement | HTMLTextAreaElement; * If element is provided, the provided element takes all its responsibilities. * DOM hierarchy isn't checked in this case, its assumed that hieararchy is correct in such a case. * - * Undefined means that the plugin decides whether the respective element needs to be added or can be savely omitted. - * True means that even if the plugin would decide to not generate the element, the element is still generated. - * False means that event if the plugin would decide to generate the element, the element won't be generated. + * Undefined means that the environment initialization strategy for the respective element is used. */ -export interface OSTargetObject { + export interface StructureInitialization { target: OSTargetElement; - host?: HTMLElement; - padding?: HTMLElement | boolean; - viewport?: HTMLElement; - content?: HTMLElement | boolean; + host?: StructureInitializationStaticElement; // only relevant for textarea + viewport?: StructureInitializationStaticElement; + padding?: StructureInitializationDynamicElement; + content?: StructureInitializationDynamicElement; } -export type OSTarget = OSTargetElement | OSTargetObject; +/** + * Object for special initialization. + * + * scrollbarsSlot is the element to which the scrollbars are applied to. If null or undefined the plugin decides by itself whats the scrollbars slot. + */ +export interface ScrollbarsInitialization { + scrollbarsSlot?: null | HTMLElement | ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => null | HTMLElement); +} + +export interface OSInitializationObject extends StructureInitialization, ScrollbarsInitialization { +} + +export type OSTarget = OSTargetElement | OSInitializationObject; /* export namespace OverlayScrollbars { diff --git a/packages/overlayscrollbars/tests/jsdom/setups/structureSetup.test.ts b/packages/overlayscrollbars/tests/jsdom/setups/structureSetup.test.ts index 77d83e2..a4cd0ab 100644 --- a/packages/overlayscrollbars/tests/jsdom/setups/structureSetup.test.ts +++ b/packages/overlayscrollbars/tests/jsdom/setups/structureSetup.test.ts @@ -1,5 +1,5 @@ -import { Environment } from 'environment'; -import { OSTarget, OSTargetObject } from 'typings'; +import { Environment, StructureInitializationStaticElement, StructureInitializationDynamicElement } from 'environment'; +import { OSTarget, StructureInitialization } from 'typings'; import { createStructureSetup, StructureSetup } from 'setups/structureSetup'; import { isHTMLElement } from 'support'; @@ -11,7 +11,7 @@ jest.mock('environment', () => { }); interface StructureSetupProxy { - input: OSTarget | OSTargetObject; + input: OSTarget | StructureInitialization; setup: StructureSetup; } @@ -84,7 +84,7 @@ const assertCorrectDOMStructure = (textarea?: boolean) => { } }; -const createStructureSetupProxy = (target: OSTarget | OSTargetObject): StructureSetupProxy => ({ +const createStructureSetupProxy = (target: OSTarget | StructureInitialization): StructureSetupProxy => ({ input: target, setup: createStructureSetup(target), }); @@ -135,11 +135,22 @@ const assertCorrectSetup = (textarea: boolean, setupProxy: StructureSetupProxy, expect(typeof _destroy).toBe('function'); const { _nativeScrollbarStyling, _cssCustomProperties, _getInitializationStrategy } = environment; - const { _padding: paddingNeeded, _content: contentNeeded } = _getInitializationStrategy(); + const { + _host: hostInitStrategy, + _viewport: viewportInitStrategy, + _padding: paddingInitStrategy, + _content: contentInitStrategy, + } = _getInitializationStrategy(); const inputIsElement = isHTMLElement(input); - const inputAsObj = input as OSTargetObject; + const inputAsObj = input as StructureInitialization; const styleElm = document.querySelector('style'); - const checkStrategyDependendElements = (elm: Element | null, input: HTMLElement | boolean | undefined, strategy: boolean) => { + const checkStrategyDependendElements = ( + elm: Element | null, + input: HTMLElement | boolean | undefined, + isStaticStrategy: boolean, + strategy: StructureInitializationStaticElement | StructureInitializationDynamicElement, + id: string + ) => { if (input) { expect(elm).toBeTruthy(); } else { @@ -147,10 +158,57 @@ const assertCorrectSetup = (textarea: boolean, setupProxy: StructureSetupProxy, expect(elm).toBeFalsy(); } if (input === undefined) { - if (strategy) { - expect(elm).toBeTruthy(); + if (isStaticStrategy) { + strategy = strategy as StructureInitializationStaticElement; + if (typeof strategy === 'function') { + const result = strategy(target); + if (result) { + expect(result).toBe(elm); + } else { + expect(elm).toBeTruthy(); + } + } else { + expect(elm).toBeTruthy(); + } } else { - expect(elm).toBeFalsy(); + strategy = strategy as StructureInitializationDynamicElement; + const expectDefaultValue = () => { + if (id === 'padding') { + if (_nativeScrollbarStyling) { + expect(elm).toBeFalsy(); + } else { + expect(elm).toBeTruthy(); + } + } else if (id === 'content') { + expect(elm).toBeFalsy(); + } + }; + if (typeof strategy === 'function') { + const result = strategy(target); + const resultIsBoolean = typeof result === 'boolean'; + if (resultIsBoolean) { + if (result) { + expect(elm).toBeTruthy(); + } else { + expect(elm).toBeFalsy(); + } + } else if (result) { + expect(elm).toBe(result); + } else { + expectDefaultValue(); + } + } else { + const strategyIsBoolean = typeof strategy === 'boolean'; + if (strategyIsBoolean) { + if (strategy) { + expect(elm).toBeTruthy(); + } else { + expect(elm).toBeFalsy(); + } + } else { + expectDefaultValue(); + } + } } } } @@ -163,12 +221,16 @@ const assertCorrectSetup = (textarea: boolean, setupProxy: StructureSetupProxy, } if (inputIsElement) { - checkStrategyDependendElements(padding, undefined, paddingNeeded); - checkStrategyDependendElements(content, undefined, contentNeeded); + checkStrategyDependendElements(padding, undefined, false, paddingInitStrategy, 'padding'); + checkStrategyDependendElements(content, undefined, false, contentInitStrategy, 'content'); + checkStrategyDependendElements(viewport, undefined, true, viewportInitStrategy, 'viewport'); + checkStrategyDependendElements(host, undefined, true, hostInitStrategy, 'host'); } else { - const { padding: inputPadding, content: inputContent } = inputAsObj; - checkStrategyDependendElements(padding, inputPadding, paddingNeeded); - checkStrategyDependendElements(content, inputContent, contentNeeded); + const { padding: inputPadding, content: inputContent, viewport: inputViewport, host: inputHost } = inputAsObj; + checkStrategyDependendElements(padding, inputPadding, false, paddingInitStrategy, 'padding'); + checkStrategyDependendElements(content, inputContent, false, contentInitStrategy, 'content'); + checkStrategyDependendElements(viewport, inputViewport, true, viewportInitStrategy, 'viewport'); + checkStrategyDependendElements(host, inputHost, true, hostInitStrategy, 'host'); } return setup; @@ -215,8 +277,11 @@ const envInitStrategyMin = { env: { ...env, _getInitializationStrategy: () => ({ - _content: false, + _host: null, + _viewport: () => null, + _content: () => false, _padding: false, + _scrollbarsSlot: null, }), }, }; @@ -225,8 +290,24 @@ const envInitStrategyMax = { env: { ...env, _getInitializationStrategy: () => ({ + _host: null, + _viewport: null, _content: true, - _padding: true, + _padding: () => true, + _scrollbarsSlot: null, + }), + }, +}; +const envInitStrategyAssigned = { + name: 'initialization strategy assigned', + env: { + ...env, + _getInitializationStrategy: () => ({ + _host: () => document.querySelector('#host1') as HTMLElement, + _viewport: (target: HTMLElement) => target.querySelector('#viewport') as HTMLElement, + _content: (target: HTMLElement) => target.querySelector('#content') as HTMLElement, + _padding: (target: HTMLElement) => target.querySelector('#padding') as HTMLElement, + _scrollbarsSlot: null, }), }, }; @@ -234,554 +315,556 @@ const envInitStrategyMax = { describe('structureSetup', () => { afterEach(() => clearBody()); - [envDefault, envNativeScrollbarStyling, envCssCustomProperties, envInitStrategyMin, envInitStrategyMax].forEach((envWithName) => { - const { env: currEnv, name } = envWithName; - describe(`Environment: ${name}`, () => { - beforeAll(() => { - mockGetEnvironment.mockImplementation(() => currEnv); - }); + [envDefault, envNativeScrollbarStyling, envCssCustomProperties, envInitStrategyMin, envInitStrategyMax, envInitStrategyAssigned].forEach( + (envWithName) => { + const { env: currEnv, name } = envWithName; + describe(`Environment: ${name}`, () => { + beforeAll(() => { + mockGetEnvironment.mockImplementation(() => currEnv); + }); - [false, true].forEach((isTextarea) => { - describe(isTextarea ? 'textarea' : 'element', () => { - describe('basic', () => { - test('Element', () => { - const snapshot = fillBody(isTextarea); - const setup = assertCorrectSetup(isTextarea, createStructureSetupProxy(getTarget(isTextarea)), currEnv); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - - test('Object', () => { - const snapshot = fillBody(isTextarea); - const setup = assertCorrectSetup(isTextarea, createStructureSetupProxy({ target: getTarget(isTextarea) }), currEnv); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - }); - - describe('complex', () => { - describe('single assigned', () => { - test('padding', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; - }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: document.querySelector('#padding')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - - test('viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; - }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - viewport: document.querySelector('#viewport')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - - test('content', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; - }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - content: document.querySelector('#content')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - }); - - describe('multiple assigned', () => { - test('padding viewport content', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; - }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: document.querySelector('#padding')!, - viewport: document.querySelector('#viewport')!, - content: document.querySelector('#content')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - - test('padding viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; - }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: document.querySelector('#padding')!, - viewport: document.querySelector('#viewport')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - - test('padding content', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; - }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: document.querySelector('#padding')!, - content: document.querySelector('#content')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - - test('viewport content', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; - }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - viewport: document.querySelector('#viewport')!, - content: document.querySelector('#content')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - }); - - describe('single false', () => { - test('padding', () => { + [false, true].forEach((isTextarea) => { + describe(isTextarea ? 'textarea' : 'element', () => { + describe('basic', () => { + test('Element', () => { const snapshot = fillBody(isTextarea); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - target: getTarget(isTextarea), - padding: false, - }), - currEnv - ); + const setup = assertCorrectSetup(isTextarea, createStructureSetupProxy(getTarget(isTextarea)), currEnv); assertCorrectDOMStructure(isTextarea); assertCorrectDestroy(snapshot, setup); }); - test('content', () => { + test('Object', () => { const snapshot = fillBody(isTextarea); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - target: getTarget(isTextarea), - content: false, - }), - currEnv - ); + const setup = assertCorrectSetup(isTextarea, createStructureSetupProxy({ target: getTarget(isTextarea) }), currEnv); assertCorrectDOMStructure(isTextarea); assertCorrectDestroy(snapshot, setup); }); }); - describe('single true', () => { - test('padding', () => { - const snapshot = fillBody(isTextarea); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - target: getTarget(isTextarea), - padding: true, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - - test('content', () => { - const snapshot = fillBody(isTextarea); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - target: getTarget(isTextarea), - content: true, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - }); - - describe('multiple false', () => { - test('padding & content', () => { - const snapshot = fillBody(isTextarea); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - target: getTarget(isTextarea), - padding: false, - content: false, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - }); - - describe('multiple true', () => { - test('padding & content', () => { - const snapshot = fillBody(isTextarea); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - target: getTarget(isTextarea), - padding: true, - content: true, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - }); - - describe('mixed', () => { - test('false: padding & content | assigned: viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + describe('complex', () => { + describe('single assigned', () => { + test('padding', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: document.querySelector('#padding')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + viewport: document.querySelector('#viewport')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('content', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + content: document.querySelector('#content')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: false, - viewport: document.querySelector('#viewport')!, - content: false, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); }); - test('true: padding & content | assigned: viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + describe('multiple assigned', () => { + test('padding viewport content', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: document.querySelector('#padding')!, + viewport: document.querySelector('#viewport')!, + content: document.querySelector('#content')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('padding viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: document.querySelector('#padding')!, + viewport: document.querySelector('#viewport')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('padding content', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: document.querySelector('#padding')!, + content: document.querySelector('#content')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('viewport content', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + viewport: document.querySelector('#viewport')!, + content: document.querySelector('#content')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: true, - viewport: document.querySelector('#viewport')!, - content: true, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); }); - test('true: content | false: padding | assigned: viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + describe('single false', () => { + test('padding', () => { + const snapshot = fillBody(isTextarea); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + target: getTarget(isTextarea), + padding: false, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('content', () => { + const snapshot = fillBody(isTextarea); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + target: getTarget(isTextarea), + content: false, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: false, - viewport: document.querySelector('#viewport')!, - content: true, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); }); - test('true: padding | false: content | assigned: viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + describe('single true', () => { + test('padding', () => { + const snapshot = fillBody(isTextarea); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + target: getTarget(isTextarea), + padding: true, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('content', () => { + const snapshot = fillBody(isTextarea); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + target: getTarget(isTextarea), + content: true, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: true, - viewport: document.querySelector('#viewport')!, - content: false, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); }); - test('false: padding | assigned: content', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + describe('multiple false', () => { + test('padding & content', () => { + const snapshot = fillBody(isTextarea); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + target: getTarget(isTextarea), + padding: false, + content: false, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: false, - content: document.querySelector('#content')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); }); - test('true: padding | assigned: content', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + describe('multiple true', () => { + test('padding & content', () => { + const snapshot = fillBody(isTextarea); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + target: getTarget(isTextarea), + padding: true, + content: true, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: true, - content: document.querySelector('#content')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); }); - test('false: padding | assigned: viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + describe('mixed', () => { + test('false: padding & content | assigned: viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: false, + viewport: document.querySelector('#viewport')!, + content: false, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: false, - viewport: document.querySelector('#viewport')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - test('true: padding | assigned: viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + test('true: padding & content | assigned: viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: true, + viewport: document.querySelector('#viewport')!, + content: true, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: true, - viewport: document.querySelector('#viewport')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - test('false: padding | assigned: viewport & content', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + test('true: content | false: padding | assigned: viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: false, + viewport: document.querySelector('#viewport')!, + content: true, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - viewport: document.querySelector('#viewport')!, - padding: false, - content: document.querySelector('#content')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - test('true: padding | assigned: viewport & content', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + test('true: padding | false: content | assigned: viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: true, + viewport: document.querySelector('#viewport')!, + content: false, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - viewport: document.querySelector('#viewport')!, - padding: true, - content: document.querySelector('#content')!, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - test('false: content | assigned: padding', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + test('false: padding | assigned: content', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: false, + content: document.querySelector('#content')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: document.querySelector('#padding')!, - content: false, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - test('true: content | assigned: padding', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + test('true: padding | assigned: content', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: true, + content: document.querySelector('#content')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: document.querySelector('#padding')!, - content: true, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - test('false: content | assigned: viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + test('false: padding | assigned: viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: false, + viewport: document.querySelector('#viewport')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - viewport: document.querySelector('#viewport')!, - content: false, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - test('true: content | assigned: viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + test('true: padding | assigned: viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: true, + viewport: document.querySelector('#viewport')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - viewport: document.querySelector('#viewport')!, - content: true, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - test('false: content | assigned: padding & viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + test('false: padding | assigned: viewport & content', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + viewport: document.querySelector('#viewport')!, + padding: false, + content: document.querySelector('#content')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: document.querySelector('#padding')!, - viewport: document.querySelector('#viewport')!, - content: false, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); - }); - test('true: content | assigned: padding & viewport', () => { - const snapshot = fillBody(isTextarea, (content, hostId) => { - return `
${content}
`; + test('true: padding | assigned: viewport & content', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + viewport: document.querySelector('#viewport')!, + padding: true, + content: document.querySelector('#content')!, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('false: content | assigned: padding', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: document.querySelector('#padding')!, + content: false, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('true: content | assigned: padding', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: document.querySelector('#padding')!, + content: true, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('false: content | assigned: viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + viewport: document.querySelector('#viewport')!, + content: false, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('true: content | assigned: viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + viewport: document.querySelector('#viewport')!, + content: true, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('false: content | assigned: padding & viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: document.querySelector('#padding')!, + viewport: document.querySelector('#viewport')!, + content: false, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); + }); + + test('true: content | assigned: padding & viewport', () => { + const snapshot = fillBody(isTextarea, (content, hostId) => { + return `
${content}
`; + }); + const setup = assertCorrectSetup( + isTextarea, + createStructureSetupProxy({ + host: document.querySelector('#host')!, + target: getTarget(isTextarea), + padding: document.querySelector('#padding')!, + viewport: document.querySelector('#viewport')!, + content: true, + }), + currEnv + ); + assertCorrectDOMStructure(isTextarea); + assertCorrectDestroy(snapshot, setup); }); - const setup = assertCorrectSetup( - isTextarea, - createStructureSetupProxy({ - host: document.querySelector('#host')!, - target: getTarget(isTextarea), - padding: document.querySelector('#padding')!, - viewport: document.querySelector('#viewport')!, - content: true, - }), - currEnv - ); - assertCorrectDOMStructure(isTextarea); - assertCorrectDestroy(snapshot, setup); }); }); }); }); }); - }); - }); + } + ); }); diff --git a/yarn.lock b/yarn.lock index 61cd5b5..650956c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8790,10 +8790,10 @@ typescript@^3.9.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== -typescript@^4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" - integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== +typescript@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" + integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw== unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4"