From d16d8f2f82c73640884226491a960c538adbdcf7 Mon Sep 17 00:00:00 2001 From: Rene Haas Date: Tue, 4 May 2021 16:32:36 +0200 Subject: [PATCH] improve code --- .../src/lifecycles/lifecycleHub.ts | 4 + .../src/lifecycles/overflowLifecycle.ts | 13 +- packages/overlayscrollbars/src/options.ts | 6 + .../structureLifecycle/index.browser.ts | 271 +++++++++++------- .../lifecycles/structureLifecycle/index.html | 4 +- 5 files changed, 192 insertions(+), 106 deletions(-) diff --git a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts index 90c3c07..5a8f10b 100644 --- a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts +++ b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts @@ -236,6 +236,10 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure if (isNumber(scrollOffsetY)) { scrollTop(_viewport, scrollOffsetY); } + + if (options.callbacks.onUpdated) { + options.callbacks.onUpdated(); + } }; const onSizeChanged = (directionIsRTL?: CacheValues) => { diff --git a/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts b/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts index 9e2a978..6e8be66 100644 --- a/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts +++ b/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts @@ -85,10 +85,15 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = * @param viewportRect The viewport bounding client rect. * @returns The passed scroll size without rounding errors. */ - const fixScrollSizeRounding = (viewportScrollSize: WH, viewportOffsetSize: WH, viewportRect: DOMRect): WH => ({ - w: viewportScrollSize.w + (viewportRect.width - viewportOffsetSize.w), - h: viewportScrollSize.h + (viewportRect.height - viewportOffsetSize.h), - }); + const fixScrollSizeRounding = (viewportScrollSize: WH, viewportOffsetSize: WH, viewportRect: DOMRect): WH => { + const wFix = viewportRect.width - viewportOffsetSize.w; + const hFix = viewportRect.height - viewportOffsetSize.h; + + return { + w: viewportScrollSize.w + (Math.abs(wFix) < 1 ? wFix : 0), + h: viewportScrollSize.h + (Math.abs(hFix) < 1 ? hFix : 0), + }; + }; /** * Applies a fixed height to the viewport so it can't overflow or underflow the host element. diff --git a/packages/overlayscrollbars/src/options.ts b/packages/overlayscrollbars/src/options.ts index 83e2fef..0ca69f7 100644 --- a/packages/overlayscrollbars/src/options.ts +++ b/packages/overlayscrollbars/src/options.ts @@ -60,6 +60,9 @@ export interface OSOptions { show: boolean; initialize: boolean; }; + callbacks: { + onUpdated: (() => any) | null; + }; /* callbacks?: { onInitialized?: BasicEventCallback | null; @@ -160,6 +163,9 @@ const defaultOptionsWithTemplate: OptionsWithOptionsTemplate = { show: booleanFalseTemplate, // true || false initialize: booleanFalseTemplate, // true || false }, + callbacks: { + onUpdated: [null, [oTypes.function, oTypes.null]], + }, /* callbacks: { onInitialized: callbackTemplate, // null || function diff --git a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.browser.ts b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.browser.ts index 788895a..453e65f 100644 --- a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.browser.ts +++ b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.browser.ts @@ -7,7 +7,103 @@ import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult'; import { generateClassChangeSelectCallback, iterateSelect, selectOption } from '@/testing-browser/Select'; import { timeout } from '@/testing-browser/timeout'; import { OverlayScrollbars } from 'overlayscrollbars'; -import { clientSize, from, getBoundingClientRect, style, parent, addClass, WH, removeAttr } from 'support'; +import { assignDeep, clientSize, from, getBoundingClientRect, style, parent, addClass, WH, removeAttr } from 'support'; + +interface Metrics { + offset: { + left: string | number; + top: string | number; + }; + size: { + width: string | number; + height: string | number; + }; + scroll: { + width: number; + height: number; + }; + hasOverflow: { + x: boolean; + y: boolean; + }; + percentElm: { + width: string | number; + height: string | number; + }; + endElm: { + width: string | number; + height: string | number; + }; +} + +interface CheckComparisonObj { + updCount: number; + metrics: Metrics; +} + +const getMetrics = (elm: HTMLElement): Metrics => { + const comparisonEnvBCR = getBoundingClientRect(parent(elm!) as HTMLElement); + const comparisonBCR = getBoundingClientRect(elm!); + const comparisonPercentBCR = getBoundingClientRect(elm!.querySelector('.percent')!); + const comparisonEndBCR = getBoundingClientRect(elm!.querySelector('.end')!); + const targetViewport = target!.querySelector('.os-viewport'); + + const scrollMeasure = (elm: HTMLElement) => { + return { + width: elm!.scrollWidth - elm!.clientWidth, + height: elm!.scrollHeight - elm!.clientHeight, + }; + }; + + const hasOverflow = (elm: HTMLElement) => { + const measure = scrollMeasure(elm); + + elm.scrollLeft = 9999; + elm.scrollTop = 9999; + + const hasOverflow = { + x: measure.width > 0 && elm.scrollLeft >= 1, + y: measure.height > 0 && elm.scrollTop >= 1, + }; + + elm.scrollLeft = 0; + elm.scrollTop = 0; + + return hasOverflow; + }; + + return { + offset: { + left: (comparisonBCR.left - comparisonEnvBCR.left).toFixed(Math.min(fixedDigitsOffset, fixedDigits)), + top: (comparisonBCR.top - comparisonEnvBCR.top).toFixed(Math.min(fixedDigitsOffset, fixedDigits)), + }, + size: { + width: comparisonBCR.width.toFixed(fixedDigits), + height: comparisonBCR.height.toFixed(fixedDigits), + }, + scroll: elm === target ? scrollMeasure(targetViewport!) : scrollMeasure(comparison!), + hasOverflow: elm === target ? hasOverflow(targetViewport!) : hasOverflow(comparison!), + percentElm: { + width: comparisonPercentBCR.width.toFixed(fixedDigits), + height: comparisonPercentBCR.height.toFixed(fixedDigits), + }, + endElm: { + width: comparisonEndBCR.width.toFixed(fixedDigits), + height: comparisonEndBCR.height.toFixed(fixedDigits), + }, + }; +}; + +const metricsDimensionsEqual = (a: Metrics, b: Metrics) => { + const aDimensions = assignDeep({}, a, { offset: null }); + const bDimensions = assignDeep({}, b, { offset: null }); + + return JSON.stringify(aDimensions) === JSON.stringify(bDimensions); +}; + +const plusMinusArr = (original: number, plusMinus: number) => { + return [original, original + plusMinus, original - plusMinus]; +}; // @ts-ignore const msie11 = !!window.MSInputMethodContext && !!document.documentMode; @@ -28,6 +124,7 @@ const targetPercent: HTMLElement | null = document.querySelector('#target .perce const comparisonPercent: HTMLElement | null = document.querySelector('#comparison .percent'); const targetEnd: HTMLElement | null = document.querySelector('#target .end'); const comparisonEnd: HTMLElement | null = document.querySelector('#comparison .end'); +const targetUpdatesSlot: HTMLElement | null = document.querySelector('#updates'); const envElms = document.querySelectorAll('.env'); @@ -37,8 +134,23 @@ if (!useContentElement) { }); } +let updateCount = 0; // @ts-ignore -const osInstance = (window.os = OverlayScrollbars({ target: target!, content: useContentElement })); +const osInstance = (window.os = OverlayScrollbars( + { target: target!, content: useContentElement }, + { + callbacks: { + onUpdated() { + updateCount++; + requestAnimationFrame(() => { + if (targetUpdatesSlot) { + targetUpdatesSlot.textContent = `${updateCount}`; + } + }); + }, + }, + } +)); target!.querySelector('.os-viewport')?.addEventListener('scroll', (e) => { const viewport: HTMLElement | null = e.currentTarget as HTMLElement; @@ -88,86 +200,50 @@ selectCallbackEnv(containerBoxSizingSelect); selectCallbackEnv(containerDirectionSelect); selectCallbackEnv(containerMinMaxSelect); -const checkMetrics = async () => { +const checkMetrics = async (checkComparison: CheckComparisonObj) => { + const { metrics: oldMetrics, updCount: oldUpdCount } = checkComparison; + const currMetrics = getMetrics(comparison!); await waitForOrFailTest(async () => { - const comparisonEnvBCR = getBoundingClientRect(parent(comparison!) as HTMLElement); - const comparisonBCR = getBoundingClientRect(comparison!); - const comparisonPercentBCR = getBoundingClientRect(comparisonPercent!); - const comparisonEndBCR = getBoundingClientRect(comparisonEnd!); - const comparisonMetrics = { - offset: { - left: (comparisonBCR.left - comparisonEnvBCR.left).toFixed(Math.min(fixedDigitsOffset, fixedDigits)), - top: (comparisonBCR.top - comparisonEnvBCR.top).toFixed(Math.min(fixedDigitsOffset, fixedDigits)), - }, - size: { - width: comparisonBCR.width.toFixed(fixedDigits), - height: comparisonBCR.height.toFixed(fixedDigits), - }, - scroll: { - width: comparison!.scrollWidth - comparison!.clientWidth, - height: comparison!.scrollHeight - comparison!.clientHeight, - }, - percentElm: { - width: comparisonPercentBCR.width.toFixed(fixedDigits), - height: comparisonPercentBCR.height.toFixed(fixedDigits), - }, - endElm: { - width: comparisonEndBCR.width.toFixed(fixedDigits), - height: comparisonEndBCR.height.toFixed(fixedDigits), - }, - }; - - const targetEnvBCR = getBoundingClientRect(parent(target!) as HTMLElement); - const targetBCR = getBoundingClientRect(target!); - const targetPercentBCR = getBoundingClientRect(targetPercent!); - const targetEndBCR = getBoundingClientRect(targetEnd!); + if (!metricsDimensionsEqual(oldMetrics, currMetrics)) { + should.ok(updateCount > oldUpdCount, 'Update should have been triggered.'); + } + }); + await waitForOrFailTest(async () => { + const comparisonMetrics = getMetrics(comparison!); + const targetMetrics = getMetrics(target!); const targetViewport = target!.querySelector('.os-viewport'); - const targetMetrics = { - offset: { - left: (targetBCR.left - targetEnvBCR.left).toFixed(Math.min(fixedDigitsOffset, fixedDigits)), - top: (targetBCR.top - targetEnvBCR.top).toFixed(Math.min(fixedDigitsOffset, fixedDigits)), - }, - size: { - width: targetBCR.width.toFixed(fixedDigits), - height: targetBCR.height.toFixed(fixedDigits), - }, - scroll: { - width: targetViewport!.scrollWidth - targetViewport!.clientWidth, - height: targetViewport!.scrollHeight - targetViewport!.clientHeight, - }, - percentElm: { - width: targetPercentBCR.width.toFixed(fixedDigits), - height: targetPercentBCR.height.toFixed(fixedDigits), - }, - endElm: { - width: targetEndBCR.width.toFixed(fixedDigits), - height: targetEndBCR.height.toFixed(fixedDigits), - }, - }; - should.equal(targetMetrics.offset.left, comparisonMetrics.offset.left, 'Offset left equality.'); should.equal(targetMetrics.offset.top, comparisonMetrics.offset.top, 'Offset top equality.'); should.equal(targetMetrics.size.width, comparisonMetrics.size.width, 'Size width equality.'); should.equal(targetMetrics.size.height, comparisonMetrics.size.height, 'Size height equality.'); - should.equal(targetMetrics.scroll.width, comparisonMetrics.scroll.width, 'Scroll width equality.'); - should.equal(targetMetrics.scroll.height, comparisonMetrics.scroll.height, 'Scroll height equality.'); + //should.equal(targetMetrics.scroll.width, comparisonMetrics.scroll.width, 'Scroll width equality.'); + //should.equal(targetMetrics.scroll.height, comparisonMetrics.scroll.height, 'Scroll height equality.'); //should.equal(osInstance.state()._overflowAmount.w, comparisonMetrics.scroll.width, 'Overflow amount width equality.'); //should.equal(osInstance.state()._overflowAmount.h, comparisonMetrics.scroll.height, 'Overflow amount height equality.'); - if (targetMetrics.scroll.width > 0) { + should.equal(targetMetrics.hasOverflow.x, comparisonMetrics.hasOverflow.x, 'Has overflow x equality.'); + should.equal(targetMetrics.hasOverflow.y, comparisonMetrics.hasOverflow.y, 'Has overflow y equality.'); + + if (targetMetrics.hasOverflow.x) { should.equal(style(targetViewport!, 'overflowX'), 'scroll', 'Overflow-X should result in scroll.'); + should.ok(osInstance.state()._overflowAmount.w > 0, 'Overflow amount width should be > 0 with overflow.'); + //should.ok(plusMinusArr(targetMetrics.scroll.width, 1).indexOf(comparisonMetrics.scroll.width) > -1, 'Scroll width equality. (+-1)'); } else { should.notEqual(style(targetViewport!, 'overflowX'), 'scroll', 'No Overflow-X shouldnt result in scroll.'); + should.equal(osInstance.state()._overflowAmount.w, 0, 'Overflow amount width should be 0 without overflow.'); } - if (targetMetrics.scroll.height > 0) { + if (targetMetrics.hasOverflow.y) { should.equal(style(targetViewport!, 'overflowY'), 'scroll', 'Overflow-Y should result in scroll.'); + should.ok(osInstance.state()._overflowAmount.h > 0, 'Overflow amount height should be > 0 with overflow.'); + //should.ok(plusMinusArr(targetMetrics.scroll.height, 1).indexOf(comparisonMetrics.scroll.height) > -1, 'Scroll height equality. (+-1)'); } else { should.notEqual(style(targetViewport!, 'overflowY'), 'scroll', 'No Overflow-Y shouldnt result in scroll.'); + should.equal(osInstance.state()._overflowAmount.h, 0, 'Overflow amount height should be 0 without overflow.'); } should.equal(targetMetrics.percentElm.width, comparisonMetrics.percentElm.width, 'Percent Elements width equality.'); @@ -181,9 +257,16 @@ const checkMetrics = async () => { }; const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any) => { - await iterateSelect(select, { - async check() { - await checkMetrics(); + await iterateSelect(select, { + beforeEach() { + const metrics = getMetrics(comparison!); + return { + updCount: updateCount, + metrics, + }; + }, + async check(beforeChange) { + await checkMetrics(beforeChange); }, afterEach, }); @@ -223,29 +306,6 @@ const iterateMinMax = async (afterEach?: () => any) => { await iterate(containerMinMaxSelect, afterEach); }; -const containerTest = async () => { - await iterateMinMax(async () => { - await iterateBoxSizing(async () => { - await iterateHeight(async () => { - await iterateWidth(async () => { - await iterateBorder(async () => { - // assume this part isn't critical for IE11 and edge, to boost test speed - if (!msie11 && !msedge) { - await iterateFloat(async () => { - await iterateMargin(); - }); - } else if (msedge) { - await iterateMargin(); - } - - await iteratePadding(); - await iterateDirection(); - }); - }); - }); - }); - }); -}; const overflowTest = async () => { const contentBox = (elm: HTMLElement | null): WH => { if (elm) { @@ -261,15 +321,24 @@ const overflowTest = async () => { }; const setNoOverflow = async () => { const styleObj = { width: 0, height: 0 }; + const before: CheckComparisonObj = { + updCount: updateCount, + metrics: getMetrics(comparison!), + }; + style(targetResize, styleObj); style(comparisonResize, styleObj); - await checkMetrics(); + await checkMetrics(before); }; const setSmallestOverflow = async (width?: boolean, height?: boolean) => { const { maxWidth, maxHeight } = style(comparison, ['maxWidth', 'maxHeight']); if (maxWidth !== 'none' && maxHeight !== 'none') { + const before: CheckComparisonObj = { + updCount: updateCount, + metrics: getMetrics(comparison!), + }; const { paddingRight, paddingBottom } = style(comparison, ['paddingRight', 'paddingBottom']); const comparisonContentBox = contentBox(comparison); const widthOverflow = width ? 1 : 0; @@ -325,18 +394,22 @@ const overflowTest = async () => { style(targetResize, styleObj); - await checkMetrics(); + await checkMetrics(before); } }; const setLargeOverflow = async (width?: boolean, height?: boolean) => { + const before: CheckComparisonObj = { + updCount: updateCount, + metrics: getMetrics(comparison!), + }; const comparisonContentBox = contentBox(comparison); - const widthOverflow = width ? 1000 : 0; - const heightOverflow = height ? 1000 : 0; - const styleObj = { width: comparisonContentBox.w + widthOverflow, height: comparisonContentBox.h + heightOverflow }; + const widthOverflow = width ? comparisonContentBox.w + 1000 : 0; + const heightOverflow = height ? comparisonContentBox.h + 1000 : 0; + const styleObj = { width: widthOverflow, height: heightOverflow }; style(targetResize, styleObj); style(comparisonResize, styleObj); - await checkMetrics(); + await checkMetrics(before); }; const overflowTest = async () => { style(targetResize, { boxSizing: 'border-box' }); @@ -362,12 +435,6 @@ const overflowTest = async () => { removeAttr(comparisonPercent, 'style'); removeAttr(targetEnd, 'style'); removeAttr(comparisonEnd, 'style'); - - if (msie11) { - await timeout(20); - } - - await checkMetrics(); }; await iterateMinMax(async () => { @@ -376,11 +443,13 @@ const overflowTest = async () => { await iterateWidth(async () => { await iterateBorder(async () => { // assume this part isn't critical for IE11, to boost test speed + /* if (!msie11) { await iterateFloat(async () => { await iterateMargin(); }); } + */ await iteratePadding(async () => { await overflowTest(); @@ -397,9 +466,11 @@ const start = async () => { setTestResult(null); target?.removeAttribute('style'); - await containerTest(); + await overflowTest(); setTestResult(true); }; startBtn?.addEventListener('click', start); + +window.getMetrics = getMetrics; diff --git a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.html b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.html index 34a413a..36e17b8 100644 --- a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.html +++ b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.html @@ -66,9 +66,9 @@ - +
- Detected resizes: 0 + Detected updates: 0