diff --git a/packages/overlayscrollbars/src/setups/structureSetup/updateSegments/overflowUpdateSegment.ts b/packages/overlayscrollbars/src/setups/structureSetup/updateSegments/overflowUpdateSegment.ts index 50a8322..22024e4 100644 --- a/packages/overlayscrollbars/src/setups/structureSetup/updateSegments/overflowUpdateSegment.ts +++ b/packages/overlayscrollbars/src/setups/structureSetup/updateSegments/overflowUpdateSegment.ts @@ -397,6 +397,7 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = ( const [showNativeOverlaidScrollbarsOption, showNativeOverlaidScrollbarsChanged] = checkOption('nativeScrollbarsOverlaid.show'); const [overflow, overflowChanged] = checkOption>('overflow'); + const showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && @@ -427,10 +428,6 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = ( fixFlexboxGlue(preMeasureViewportOverflowState, _heightIntrinsic); } - if (overflowVisible) { - removeClass(_viewport, classNameOverflowVisible); - } - if ( _sizeChanged || _paddingStyleChanged || @@ -438,6 +435,10 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = ( _directionChanged || showNativeOverlaidScrollbarsChanged ) { + if (overflowVisible) { + removeClass(_viewport, classNameOverflowVisible); + } + const [redoViewportArrange, undoViewportArrangeOverflowState] = undoViewportArrange( showNativeOverlaidScrollbars, _directionIsRTL, diff --git a/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.browser.ts b/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.browser.ts index 80f6b7f..cf7ab0b 100644 --- a/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.browser.ts +++ b/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.browser.ts @@ -18,6 +18,8 @@ import { resize } from '@/testing-browser/Resize'; import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult'; import { generateClassChangeSelectCallback, iterateSelect } from '@/testing-browser/Select'; import { timeout } from '@/testing-browser/timeout'; +import { OSOptions } from 'options'; +import { PartialOptions } from 'typings'; interface Metrics { offset: { @@ -53,6 +55,70 @@ interface CheckComparisonObj { const isFractionalPixelRatio = () => window.devicePixelRatio % 1 !== 0; +const isVisibleOverflow = (val: string) => val.indexOf('visible') === 0; + +const expectedOverflowVisibleBehavior = ( + overflowOptionAxis: string, + hasOverflowOtherAxis: boolean +) => { + const overflowVisibleBehavior = overflowOptionAxis.replace('visible', '').slice(1); + return hasOverflowOtherAxis ? overflowVisibleBehavior || 'hidden' : 'visible'; +}; + +// @ts-ignore +const msie11 = !!window.MSInputMethodContext && !!document.documentMode; +const msedge = window.navigator.userAgent.toLowerCase().indexOf('edge') > -1; + +msie11 && addClass(document.body, 'msie11'); + +const useContentElement = false; +const fixedDigits = msie11 ? 1 : 3; +const fixedDigitsOffset = 3; + +const startBtn: HTMLButtonElement | null = document.querySelector('#start'); +const target: HTMLElement | null = document.querySelector('#target'); +const targetMetrics: HTMLElement | null = document.querySelector('#targetMetrics'); +const comparison: HTMLElement | null = document.querySelector('#comparison'); +const comparisonMetrics: HTMLElement | null = document.querySelector('#comparisonMetrics'); +const targetResize: HTMLElement | null = document.querySelector('#target .resize'); +const comparisonResize: HTMLElement | null = document.querySelector('#comparison .resize'); +const targetPercent: HTMLElement | null = document.querySelector('#target .percent'); +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 targetOptionsSlot: HTMLElement | null = document.querySelector('#options'); +const targetUpdatesSlot: HTMLElement | null = document.querySelector('#updates'); + +const envElms = document.querySelectorAll('.env'); + +if (!useContentElement) { + envElms.forEach((elm) => { + addClass(elm, 'intrinsic-hack'); + }); +} + +let updateCount = 0; +// @ts-ignore +const osInstance = + // @ts-ignore + (window.os = OverlayScrollbars( + { target: target!, content: useContentElement }, + { nativeScrollbarsOverlaid: { initialize: true } }, + { + updated() { + updateCount++; + requestAnimationFrame(() => { + if (targetUpdatesSlot) { + targetUpdatesSlot.textContent = `${updateCount}`; + } + if (targetOptionsSlot) { + targetOptionsSlot.textContent = JSON.stringify(osInstance.options().overflow, null, 2); + } + }); + }, + } + )); + const getMetrics = (elm: HTMLElement): Metrics => { // const rounding = isFractionalPixelRatio() ? Math.round : (num: number) => num; const elmIsTarget = elm === target; @@ -128,66 +194,6 @@ const metricsDimensionsEqual = (a: Metrics, b: Metrics) => { return JSON.stringify(aDimensions) === JSON.stringify(bDimensions); }; -const isVisibleOverflow = (val: string) => val.indexOf('visible') === 0; - -const expectedOverflowVisibleBehavior = ( - overflowOptionAxis: string, - hasOverflowOtherAxis: boolean -) => { - const overflowVisibleBehavior = overflowOptionAxis.replace('visible', '').slice(1); - return hasOverflowOtherAxis ? overflowVisibleBehavior || 'hidden' : 'visible'; -}; - -// @ts-ignore -const msie11 = !!window.MSInputMethodContext && !!document.documentMode; -const msedge = window.navigator.userAgent.toLowerCase().indexOf('edge') > -1; - -msie11 && addClass(document.body, 'msie11'); - -const useContentElement = false; -const fixedDigits = msie11 ? 1 : 3; -const fixedDigitsOffset = 3; - -const startBtn: HTMLButtonElement | null = document.querySelector('#start'); -const target: HTMLElement | null = document.querySelector('#target'); -const targetMetrics: HTMLElement | null = document.querySelector('#targetMetrics'); -const comparison: HTMLElement | null = document.querySelector('#comparison'); -const comparisonMetrics: HTMLElement | null = document.querySelector('#comparisonMetrics'); -const targetResize: HTMLElement | null = document.querySelector('#target .resize'); -const comparisonResize: HTMLElement | null = document.querySelector('#comparison .resize'); -const targetPercent: HTMLElement | null = document.querySelector('#target .percent'); -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'); - -if (!useContentElement) { - envElms.forEach((elm) => { - addClass(elm, 'intrinsic-hack'); - }); -} - -let updateCount = 0; -// @ts-ignore -const osInstance = - // @ts-ignore - (window.os = OverlayScrollbars( - { target: target!, content: useContentElement }, - { nativeScrollbarsOverlaid: { initialize: true } }, - { - updated() { - updateCount++; - requestAnimationFrame(() => { - if (targetUpdatesSlot) { - targetUpdatesSlot.textContent = `${updateCount}`; - } - }); - }, - } - )); - target!.querySelector('.os-viewport')?.addEventListener('scroll', (e) => { const viewport: HTMLElement | null = e.currentTarget as HTMLElement; comparison!.scrollLeft = viewport.scrollLeft; @@ -445,8 +451,13 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => { }); }; -const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any) => { +const iterate = async ( + select: HTMLSelectElement | null, + afterEach?: () => any, + skippedItems?: string[] +) => { await iterateSelect(select, { + filter: (item: string) => !skippedItems?.includes(item), beforeEach() { const metrics = getMetrics(comparison!); return { @@ -468,22 +479,22 @@ const iterateEnvHeight = async (afterEach?: () => any) => { await iterate(envHeightSelect, afterEach); }; */ -const iterateHeight = async (afterEach?: () => any) => { - await iterate(containerHeightSelect, afterEach); +const iterateHeight = async (afterEach?: () => any, skippedItems?: string[]) => { + await iterate(containerHeightSelect, afterEach, skippedItems); }; -const iterateWidth = async (afterEach?: () => any) => { - await iterate(containerWidthSelect, afterEach); +const iterateWidth = async (afterEach?: () => any, skippedItems?: string[]) => { + await iterate(containerWidthSelect, afterEach, skippedItems); }; /* const iterateFloat = async (afterEach?: () => any) => { await iterate(containerFloatSelect, afterEach); }; */ -const iteratePadding = async (afterEach?: () => any) => { - await iterate(containerPaddingSelect, afterEach); +const iteratePadding = async (afterEach?: () => any, skippedItems?: string[]) => { + await iterate(containerPaddingSelect, afterEach, skippedItems); }; -const iterateBorder = async (afterEach?: () => any) => { - await iterate(containerBorderSelect, afterEach); +const iterateBorder = async (afterEach?: () => any, skippedItems?: string[]) => { + await iterate(containerBorderSelect, afterEach, skippedItems); }; /* const iterateMargin = async (afterEach?: () => any) => { @@ -500,7 +511,7 @@ const iterateMinMax = async (afterEach?: () => any) => { await iterate(containerMinMaxSelect, afterEach); }; -const overflowTest = async () => { +const overflowTest = async (osOptions?: PartialOptions) => { const additiveOverflow = () => { if (isFractionalPixelRatio()) { return 1; @@ -630,7 +641,7 @@ const overflowTest = async () => { await checkMetrics(before); }; - const overflowTest = async () => { + const iterateOverflow = async () => { style(targetResize, { boxSizing: 'border-box' }); style(comparisonResize, { boxSizing: 'border-box' }); style(targetPercent, { display: 'none' }); @@ -656,27 +667,45 @@ const overflowTest = async () => { removeAttr(comparisonEnd, 'style'); }; - await iterateMinMax(async () => { - await iterateBoxSizing(async () => { - await iterateHeight(async () => { - await iterateWidth(async () => { - await iterateBorder(async () => { - // assume this part isn't critical - /* + if (osOptions) { + osInstance.options(osOptions); + + await iterateMinMax(async () => { + await iterateBoxSizing(async () => { + await iterateHeight(async () => { + await iterateWidth(async () => { + await iterateBorder(async () => { + await iteratePadding(async () => { + await iterateOverflow(); + }, ['paddingLarge']); + }, ['borderSmall']); + }, ['widthHundred']); + }, ['heightHundred']); + }); + }); + } else { + await iterateMinMax(async () => { + await iterateBoxSizing(async () => { + await iterateHeight(async () => { + await iterateWidth(async () => { + await iterateBorder(async () => { + // assume this part isn't critical + /* await iterateFloat(async () => { await iterateMargin(); }); */ - await iteratePadding(async () => { - await overflowTest(); + await iteratePadding(async () => { + await iterateOverflow(); + }); + await iterateDirection(); }); - await iterateDirection(); }); }); }); }); - }); + } }; const start = async () => { @@ -685,6 +714,16 @@ const start = async () => { target?.removeAttribute('style'); await overflowTest(); + await overflowTest({ overflow: { x: 'hidden', y: 'scroll' } }); + await overflowTest({ overflow: { x: 'scroll', y: 'hidden' } }); + await overflowTest({ overflow: { x: 'visible', y: 'scroll' } }); + await overflowTest({ overflow: { x: 'scroll', y: 'visible' } }); + await overflowTest({ overflow: { x: 'visible', y: 'visible' } }); + await overflowTest({ overflow: { x: 'visible-scroll', y: 'visible-hidden' } }); + await overflowTest({ overflow: { x: 'visible-hidden', y: 'hidden' } }); + await overflowTest({ overflow: { x: 'visible', y: 'visible-scroll' } }); + await overflowTest({ overflow: { x: 'scroll', y: 'visible-scroll' } }); + setTestResult(true); }; diff --git a/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.html b/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.html index 53002a6..7f4078c 100644 --- a/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.html +++ b/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.html @@ -68,6 +68,7 @@
+ UsedOptions: 0 Detected updates: 0
diff --git a/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.scss b/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.scss index 010f577..3828b7b 100644 --- a/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.scss +++ b/packages/overlayscrollbars/tests/playwright/lifecycles/structureLifecycle/index.scss @@ -1,6 +1,7 @@ body { display: flex; flex-direction: column; + overflow: scroll; } #controls { flex: none; diff --git a/packages/testing-browser/src/Select.ts b/packages/testing-browser/src/Select.ts index 007b11e..9360823 100644 --- a/packages/testing-browser/src/Select.ts +++ b/packages/testing-browser/src/Select.ts @@ -7,25 +7,34 @@ const noop = (): T => { return {} as T; }; -const getSelectOptions = (selectElement: HTMLSelectElement) => Array.from(selectElement.options).map((option) => option.value); +const getSelectOptions = (selectElement: HTMLSelectElement) => + Array.from(selectElement.options).map((option) => option.value); -export const generateSelectCallback = ( - targetElms: HTMLElement[] | HTMLElement | null, - callback: (targetAffectedElm: HTMLElement, possibleValues: string[], selectedValue: string) => any -) => (event: Event | HTMLSelectElement | null) => { - const target: HTMLSelectElement | null = isEvent(event) ? (event.target as HTMLSelectElement) : event; - if (target) { - const selectedOption = target.value; - const selectOptions = getSelectOptions(target); - const elmsArr = Array.isArray(targetElms) ? targetElms : [targetElms]; +export const generateSelectCallback = + ( + targetElms: HTMLElement[] | HTMLElement | null, + callback: ( + targetAffectedElm: HTMLElement, + possibleValues: string[], + selectedValue: string + ) => any + ) => + (event: Event | HTMLSelectElement | null) => { + const target: HTMLSelectElement | null = isEvent(event) + ? (event.target as HTMLSelectElement) + : event; + if (target) { + const selectedOption = target.value; + const selectOptions = getSelectOptions(target); + const elmsArr = Array.isArray(targetElms) ? targetElms : [targetElms]; - elmsArr.forEach((elm) => { - if (elm) { - callback(elm, selectOptions, selectedOption); - } - }); - } -}; + elmsArr.forEach((elm) => { + if (elm) { + callback(elm, selectOptions, selectedOption); + } + }); + } + }; export const generateClassChangeSelectCallback = (targetElms: HTMLElement[] | HTMLElement | null) => generateSelectCallback(targetElms, (targetAffectedElm, possibleValues, selectedValue) => { @@ -33,7 +42,10 @@ export const generateClassChangeSelectCallback = (targetElms: HTMLElement[] | HT targetAffectedElm.classList.add(selectedValue); }); -export const selectOption = (select: HTMLSelectElement | null, selectedOption: string | number): boolean => { +export const selectOption = ( + select: HTMLSelectElement | null, + selectedOption: string | number +): boolean => { if (!select) { return false; } @@ -47,7 +59,11 @@ export const selectOption = (select: HTMLSelectElement | null, selectedOption: s if (typeof selectedOption === 'string' && options.includes(selectedOption)) { select.value = selectedOption; - } else if (typeof selectedOption === 'number' && options.length < selectedOption && selectedOption > -1) { + } else if ( + typeof selectedOption === 'number' && + options.length < selectedOption && + selectedOption > -1 + ) { select.selectedIndex = selectedOption; } @@ -66,16 +82,19 @@ export const selectOption = (select: HTMLSelectElement | null, selectedOption: s export const iterateSelect = async ( select: HTMLSelectElement | null, options?: { + filter?: (value: string, index: number, array: string[]) => boolean; beforeEach?: () => T | Promise; check?: (input: T, selectedOptions: string) => void | Promise; afterEach?: () => void | Promise; } ) => { if (select) { - const { beforeEach = noop, check = noop, afterEach = noop } = options || {}; + const { beforeEach = noop, check = noop, afterEach = noop, filter } = options || {}; const selectOptions = getSelectOptions(select); const selectOptionsReversed = getSelectOptions(select).reverse(); - const iterateOptions = [...selectOptions, ...selectOptionsReversed]; + const iterateOptions = [...selectOptions, ...selectOptionsReversed].filter( + filter || (() => true) + ); for (let i = 0; i < iterateOptions.length; i++) { const option = iterateOptions[i]; // eslint-disable-next-line