diff --git a/packages/overlayscrollbars/src/index.ts b/packages/overlayscrollbars/src/index.ts index 0f35efe..aa80cd1 100644 --- a/packages/overlayscrollbars/src/index.ts +++ b/packages/overlayscrollbars/src/index.ts @@ -1,6 +1,7 @@ import { createDOM } from 'support/dom'; import { getEnvironment } from 'environment'; import { createSizeObserver } from 'overlayscrollbars/observers/SizeObserver'; +import { createTrinsicObserver } from 'overlayscrollbars/observers/TrinsicObserver'; const abc = { a: 1, @@ -12,6 +13,7 @@ export default () => { return [ getEnvironment(), createSizeObserver(document.body, () => {}), + createTrinsicObserver(document.body, () => {}), createDOM( '\
\ diff --git a/packages/overlayscrollbars/src/overlayscrollbars.scss b/packages/overlayscrollbars/src/overlayscrollbars.scss index eae946d..e31bb56 100644 --- a/packages/overlayscrollbars/src/overlayscrollbars.scss +++ b/packages/overlayscrollbars/src/overlayscrollbars.scss @@ -1,4 +1,5 @@ @import './sizeobserver.scss'; +@import './trinsicobserver.scss'; #os-environment { position: fixed; diff --git a/packages/overlayscrollbars/src/overlayscrollbars/observers/SizeObserver.ts b/packages/overlayscrollbars/src/overlayscrollbars/observers/SizeObserver.ts index 297d45f..914d8fa 100644 --- a/packages/overlayscrollbars/src/overlayscrollbars/observers/SizeObserver.ts +++ b/packages/overlayscrollbars/src/overlayscrollbars/observers/SizeObserver.ts @@ -52,7 +52,7 @@ export const createSizeObserver = ( onSizeChangedCallback(dir === true); }; const offListeners: (() => void)[] = []; - let appearCallback: (...args: any) => any = onSizeChangedCallbackProxy; + let appearCallback: ((...args: any) => any) | null = appear ? onSizeChangedCallbackProxy : null; if (ResizeObserverConstructor) { const resizeObserverInstance = new ResizeObserverConstructor(onSizeChangedCallbackProxy); @@ -81,7 +81,9 @@ export const createSizeObserver = ( }; const onResized = function () { rAFId = 0; - if (!isDirty) return; + if (!isDirty) { + return; + } cacheSize = currSize; onSizeChangedCallbackProxy(); @@ -114,7 +116,7 @@ export const createSizeObserver = ( height: scrollAmount, }); reset(); - appearCallback = onScroll; + appearCallback = appear ? onScroll : reset; } if (direction) { @@ -140,7 +142,8 @@ export const createSizeObserver = ( ); } - if (appear) { + // appearCallback is always needed on scroll-observer strategy to reset it + if (appearCallback) { addClass(sizeObserver, classNameSizeObserverAppear); offListeners.push(on(sizeObserver, animationStartEventName, appearCallback)); } diff --git a/packages/overlayscrollbars/src/overlayscrollbars/observers/TrinsicObserver.ts b/packages/overlayscrollbars/src/overlayscrollbars/observers/TrinsicObserver.ts index 1adf14b..88b09f9 100644 --- a/packages/overlayscrollbars/src/overlayscrollbars/observers/TrinsicObserver.ts +++ b/packages/overlayscrollbars/src/overlayscrollbars/observers/TrinsicObserver.ts @@ -36,6 +36,7 @@ export const createTrinsicObserver = ( createSizeObserver(trinsicObserver, () => { const newSize = offsetSize(trinsicObserver); const newHeightIntrinsic = newSize.h === 0; + if (newHeightIntrinsic !== heightIntrinsic) { onTrinsicChangedCallback(false, newSize.h === 0); heightIntrinsic = newHeightIntrinsic; diff --git a/packages/overlayscrollbars/tests/puppeteer/SizeObserver/index.browser.ts b/packages/overlayscrollbars/tests/puppeteer/SizeObserver/index.browser.ts index 7ac4b42..8237b89 100644 --- a/packages/overlayscrollbars/tests/puppeteer/SizeObserver/index.browser.ts +++ b/packages/overlayscrollbars/tests/puppeteer/SizeObserver/index.browser.ts @@ -75,7 +75,7 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any) if (dimensions && (offsetSizeChanged || dirChanged)) { await waitFor( - async () => { + () => { if (offsetSizeChanged) { should.equal(sizeIterations, currSizeIterations + 1); } diff --git a/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.browser.ts b/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.browser.ts new file mode 100644 index 0000000..bdfbbf5 --- /dev/null +++ b/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.browser.ts @@ -0,0 +1,141 @@ +import 'overlayscrollbars.scss'; +import './index.scss'; +import should from 'should'; +import { waitFor } from '@testing-library/dom'; +import { generateSelectCallback, iterateSelect, selectOption } from '@/testing-browser/Select'; +import { timeout } from '@/testing-browser/timeout'; +import { setTestResult } from '@/testing-browser/TestResult'; +import { offsetSize } from 'support'; + +import { createTrinsicObserver } from 'overlayscrollbars/observers/TrinsicObserver'; + +const waitForOptions = { + onTimeout(error: Error): Error { + setTestResult(false); + return error; + }, +}; + +let heightIterations = 0; +let heightIntrinsicCache: boolean; +const envElm = document.querySelector('#env'); +const targetElm = document.querySelector('#target'); +const checkElm = document.querySelector('#check'); +const envHeightSelect: HTMLSelectElement | null = document.querySelector('#envHeight'); +const targetHeightSelect: HTMLSelectElement | null = document.querySelector('#targetHeight'); +const displaySelect: HTMLSelectElement | null = document.querySelector('#display'); +const startBtn: HTMLButtonElement | null = document.querySelector('#start'); +const changesSlot: HTMLButtonElement | null = document.querySelector('#changes'); + +const envElmSelectCallback = generateSelectCallback(envElm as HTMLElement); +const targetElmSelectCallback = generateSelectCallback(targetElm as HTMLElement); + +envHeightSelect?.addEventListener('change', envElmSelectCallback); +targetHeightSelect?.addEventListener('change', targetElmSelectCallback); +displaySelect?.addEventListener('change', targetElmSelectCallback); + +envElmSelectCallback(envHeightSelect); +targetElmSelectCallback(targetHeightSelect); +targetElmSelectCallback(displaySelect); + +const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any) => { + interface IterateSelect { + currHeightIterations: number; + currHeightIntrinsic: boolean; + } + + await iterateSelect(select, { + beforeEach() { + const currHeightIterations = heightIterations; + const currHeightIntrinsic = offsetSize(checkElm as HTMLElement).h === 0; + return { + currHeightIterations, + currHeightIntrinsic, + }; + }, + async check({ currHeightIterations, currHeightIntrinsic }) { + const newHeightIntrinsic = offsetSize(checkElm as HTMLElement).h === 0; + const trinsicHeightChanged = newHeightIntrinsic !== currHeightIntrinsic; + + await waitFor(() => { + if (trinsicHeightChanged) { + should.equal(heightIterations, currHeightIterations + 1); + } + }, waitForOptions); + }, + afterEach, + }); +}; + +const iterateEnvHeight = async (afterEach?: () => any) => { + await iterate(envHeightSelect, afterEach); +}; +const iterateTargetHeight = async (afterEach?: () => any) => { + await iterate(targetHeightSelect, afterEach); +}; +const changeWhileHidden = async () => { + selectOption(targetHeightSelect as HTMLSelectElement, 'targetHeightHundred'); + + const autoToHundred = async () => { + selectOption(envHeightSelect as HTMLSelectElement, 'envHeightAuto'); + selectOption(displaySelect as HTMLSelectElement, 'displayNone'); + + await timeout(250); + + selectOption(envHeightSelect as HTMLSelectElement, 'envHeightHundred'); + selectOption(displaySelect as HTMLSelectElement, 'displayBlock'); + + await waitFor(() => { + should.equal(heightIntrinsicCache, false); + }, waitForOptions); + }; + + const hundredToAuto = async () => { + selectOption(envHeightSelect as HTMLSelectElement, 'envHeightHundred'); + selectOption(displaySelect as HTMLSelectElement, 'displayNone'); + + await timeout(250); + + selectOption(envHeightSelect as HTMLSelectElement, 'envHeightAuto'); + selectOption(displaySelect as HTMLSelectElement, 'displayBlock'); + + await waitFor(() => { + should.equal(heightIntrinsicCache, true); + }, waitForOptions); + }; + + await autoToHundred(); + await hundredToAuto(); + await autoToHundred(); + await hundredToAuto(); +}; + +const start = async () => { + setTestResult(null); + + targetElm?.removeAttribute('style'); + await iterateEnvHeight(); + await iterateTargetHeight(); + await iterateEnvHeight(async () => { + await iterateTargetHeight(); + }); + await changeWhileHidden(); + + setTestResult(true); +}; + +startBtn?.addEventListener('click', start); + +createTrinsicObserver(targetElm as HTMLElement, (widthIntrinsic: boolean, heightIntrinsic: boolean) => { + if (heightIntrinsic !== heightIntrinsicCache) { + heightIterations += 1; + heightIntrinsicCache = heightIntrinsic; + } + requestAnimationFrame(() => { + if (changesSlot) { + changesSlot.textContent = heightIterations.toString(); + } + }); +}); + +export { start }; diff --git a/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.html b/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.html new file mode 100644 index 0000000..e208a2d --- /dev/null +++ b/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.html @@ -0,0 +1,31 @@ +
+ + + + + + + + + Detected changes: 0 +
+
+
+
+
+
+
+
+
+
diff --git a/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.scss b/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.scss new file mode 100644 index 0000000..5f44e4c --- /dev/null +++ b/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.scss @@ -0,0 +1,67 @@ +body { + display: flex; + flex-direction: column; +} +#controls { + flex: none; +} +#stage { + flex: auto; + position: relative; + + & > div { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: lightgoldenrodyellow; + } +} + +#canvas > div { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +#env { + background: rgba(0, 0, 0, 0.3); +} + +#target { + background: rgba(0, 0, 0, 0.3); + overflow: hidden; + position: relative; + // prevent container from reaching 0x0 dimensions for testing purposes + min-width: 50px; + min-height: 50px; +} + +#check { + height: 100%; +} + +.envHeightAuto, +.targetHeightAuto { + height: auto; +} + +.envHeight200, +.targetHeight200 { + height: 200px; +} + +.envHeightHundred, +.targetHeightHundred { + height: 100%; +} + +.displayNone { + display: none; +} +.displayBlock { + display: block; +} diff --git a/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.test.ts b/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.test.ts new file mode 100644 index 0000000..0d2ffb1 --- /dev/null +++ b/packages/overlayscrollbars/tests/puppeteer/TrinsicObserver/index.test.ts @@ -0,0 +1,15 @@ +import expectPuppeteer from 'expect-puppeteer'; +import url from './.build/build.html'; + +describe('Environment', () => { + beforeAll(async () => { + await page.goto(url); + }); + + it('test', async () => { + await expectPuppeteer(page).toClick('#start'); + await expectPuppeteer(page).toMatchElement('#testResult.passed', { + timeout: 60000, + }); + }, 60000); +});