improve code, add ignoreMutation option

This commit is contained in:
Rene Haas
2022-07-07 09:36:22 +02:00
parent 95c0d50ddf
commit 66c55f98e9
13 changed files with 235 additions and 92 deletions
@@ -20,20 +20,23 @@ type DOMTargetObserverCallback = (targetChangedAttrs: string[], targetStyleChang
interface DOMObserverOptionsBase { interface DOMObserverOptionsBase {
_attributes?: string[]; _attributes?: string[];
_styleChangingAttributes?: string[]; _styleChangingAttributes?: string[];
/**
* A function which can ignore a changed attribute if it returns true.
* for DOMTargetObserver this applies to the changes to the observed target
* for DOMContentObserver this applies to changes to nested targets -> nested targets are elements which match the "_nestedTargetSelector" selector
*/
_ignoreTargetChange?: DOMObserverIgnoreTargetChange;
} }
interface DOMContentObserverOptions extends DOMObserverOptionsBase { interface DOMContentObserverOptions extends DOMObserverOptionsBase {
_eventContentChange?: DOMObserverEventContentChange; // [selector, eventname(s) | function returning eventname(s)] -> eventnames divided by whitespaces _eventContentChange?: DOMObserverEventContentChange; // [selector, eventname(s) | function returning eventname(s)] -> eventnames divided by whitespaces
_nestedTargetSelector?: string; _nestedTargetSelector?: string;
_ignoreContentChange?: DOMObserverIgnoreContentChange; // function which will prevent marking certain dom changes as content change if it returns true _ignoreContentChange?: DOMObserverIgnoreContentChange; // function which will prevent marking certain dom changes as content change if it returns true
_ignoreNestedTargetChange?: DOMObserverIgnoreTargetChange; // a function which will prevent marking certain attributes as changed on nested targets if it returns true
} }
interface DOMTargetObserverOptions extends DOMObserverOptionsBase { type DOMTargetObserverOptions = DOMObserverOptionsBase;
_ignoreTargetChange?: DOMObserverIgnoreTargetChange; // a function which will prevent marking certain attributes as changed if it returns true
}
type ContentChangeArrayItem = [string?, string?] | null | undefined; type ContentChangeArrayItem = [selector?: string, eventNames?: string] | null | undefined;
export type DOMObserverEventContentChange = export type DOMObserverEventContentChange =
| Array<ContentChangeArrayItem> | Array<ContentChangeArrayItem>
@@ -161,7 +164,6 @@ export const createDOMObserver = <ContentObserver extends boolean>(
_eventContentChange, _eventContentChange,
_nestedTargetSelector, _nestedTargetSelector,
_ignoreTargetChange, _ignoreTargetChange,
_ignoreNestedTargetChange,
_ignoreContentChange, _ignoreContentChange,
} = (options as DOMContentObserverOptions & DOMTargetObserverOptions) || {}; } = (options as DOMContentObserverOptions & DOMTargetObserverOptions) || {};
const [destroyEventContentChange, updateEventContentChangeElements] = createEventContentChange( const [destroyEventContentChange, updateEventContentChangeElements] = createEventContentChange(
@@ -182,8 +184,7 @@ export const createDOMObserver = <ContentObserver extends boolean>(
const finalStyleChangingAttributes = _styleChangingAttributes || []; const finalStyleChangingAttributes = _styleChangingAttributes || [];
const observedAttributes = finalAttributes.concat(finalStyleChangingAttributes); const observedAttributes = finalAttributes.concat(finalStyleChangingAttributes);
const observerCallback = (mutations: MutationRecord[]) => { const observerCallback = (mutations: MutationRecord[]) => {
const ignoreTargetChange = const ignoreTargetChange = _ignoreTargetChange || noop;
(isContentObserver ? _ignoreNestedTargetChange : _ignoreTargetChange) || noop;
const ignoreContentChange = _ignoreContentChange || noop; const ignoreContentChange = _ignoreContentChange || noop;
const targetChangedAttrs: string[] = []; const targetChangedAttrs: string[] = [];
const totalAddedNodes: Node[] = []; const totalAddedNodes: Node[] = [];
+5 -3
View File
@@ -41,9 +41,10 @@ export type UpdatedCallback = (this: any, args?: UpdatedArgs) => void;
export interface OSOptions { export interface OSOptions {
paddingAbsolute: boolean; paddingAbsolute: boolean;
updating: { updating: {
elementEvents: Array<[string, string]> | null; elementEvents: Array<[elementSelector: string, eventNames: string]> | null;
attributes: string[] | null; attributes: string[] | null;
debounce: number | [number, number] | null; debounce: [timeout: number, maxWait: number] | number | null; // (if tuple: [timeout: 0, maxWait: 33], if number: [timeout: number, maxWait: false]) debounce for content Changes
ignoreMutation: ((mutation: MutationRecord) => any) | null;
}; };
overflow: { overflow: {
x: OverflowBehavior; x: OverflowBehavior;
@@ -97,8 +98,9 @@ export const defaultOptions: OSOptions = {
paddingAbsolute: false, // true || false paddingAbsolute: false, // true || false
updating: { updating: {
elementEvents: [['img', 'load']], // array of tuples || null elementEvents: [['img', 'load']], // array of tuples || null
attributes: null,
debounce: [0, 33], // number || number array || null debounce: [0, 33], // number || number array || null
attributes: null, // string array || null
ignoreMutation: null, // () => any || null
}, },
overflow: { overflow: {
x: 'scroll', // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s x: 'scroll', // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
@@ -62,6 +62,14 @@ export interface OverlayScrollbarsState {
hasOverflow: XY<boolean>; hasOverflow: XY<boolean>;
} }
export interface OverlayScrollbarsElements {
target: HTMLElement;
host: HTMLElement;
padding: HTMLElement;
viewport: HTMLElement;
content: HTMLElement;
}
export interface OverlayScrollbars { export interface OverlayScrollbars {
options(): OSOptions; options(): OSOptions;
options(newOptions?: PartialOptions<OSOptions>): OSOptions; options(newOptions?: PartialOptions<OSOptions>): OSOptions;
@@ -70,6 +78,7 @@ export interface OverlayScrollbars {
destroy(): void; destroy(): void;
state(): OverlayScrollbarsState; state(): OverlayScrollbarsState;
elements(): OverlayScrollbarsElements;
on: AddOSEventListener; on: AddOSEventListener;
off: RemoveOSEventListener; off: RemoveOSEventListener;
@@ -177,7 +186,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
}, },
on: addEvent, on: addEvent,
off: removeEvent, off: removeEvent,
state: () => { state() {
const { _overflowAmount, _overflowStyle, _hasOverflow, _padding, _paddingAbsolute } = const { _overflowAmount, _overflowStyle, _hasOverflow, _padding, _paddingAbsolute } =
structureState(); structureState();
return assignDeep( return assignDeep(
@@ -191,6 +200,19 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
} }
); );
}, },
elements() {
const { _target, _host, _padding, _viewport, _content } = structureState._elements;
return assignDeep(
{},
{
target: _target,
host: _host,
padding: _padding || _viewport,
viewport: _viewport,
content: _content || _viewport,
}
);
},
update(force?: boolean) { update(force?: boolean) {
update({}, force); update({}, force);
}, },
@@ -25,6 +25,7 @@ const optionsTemplate: OptionsTemplate<OSOptions> = {
elementEvents: arrayNullValues, // array of tuples || null elementEvents: arrayNullValues, // array of tuples || null
attributes: arrayNullValues, attributes: arrayNullValues,
debounce: [oTypes.number, oTypes.array, oTypes.null], // number || number array || null debounce: [oTypes.number, oTypes.array, oTypes.null], // number || number array || null
ignoreMutation: [oTypes.function, oTypes.null], // function || null
}, },
overflow: { overflow: {
x: overflowAllowedValues, // visible-hidden || visible-scroll || hidden || scrol x: overflowAllowedValues, // visible-hidden || visible-scroll || hidden || scrol
@@ -3,7 +3,7 @@ import { type, isArray, isUndefined, isPlainObject, isString } from 'support/uti
import { PlainObject, PartialOptions } from 'typings'; import { PlainObject, PartialOptions } from 'typings';
export type OptionsObjectType = Record<string, unknown>; export type OptionsObjectType = Record<string, unknown>;
export type OptionsFunctionType = (this: unknown, ...args: unknown[]) => unknown; export type OptionsFunctionType = (this: any, ...args: any[]) => any;
export type OptionsTemplateType<T extends OptionsTemplateNativeTypes> = ExtractPropsKey< export type OptionsTemplateType<T extends OptionsTemplateNativeTypes> = ExtractPropsKey<
OptionsTemplateTypeMap, OptionsTemplateTypeMap,
T T
@@ -1,5 +1,4 @@
import { import {
diffClass,
debounce, debounce,
isArray, isArray,
isNumber, isNumber,
@@ -19,14 +18,10 @@ import {
removeClass, removeClass,
addClass, addClass,
hasClass, hasClass,
isFunction,
} from 'support'; } from 'support';
import { getEnvironment } from 'environment'; import { getEnvironment } from 'environment';
import { import { dataAttributeHost, classNameViewport, classNameOverflowVisible } from 'classnames';
dataAttributeHost,
classNameViewport,
classNameContent,
classNameOverflowVisible,
} from 'classnames';
import { createSizeObserver, SizeObserverCallbackParams } from 'observers/sizeObserver'; import { createSizeObserver, SizeObserverCallbackParams } from 'observers/sizeObserver';
import { createTrinsicObserver } from 'observers/trinsicObserver'; import { createTrinsicObserver } from 'observers/trinsicObserver';
import { createDOMObserver, DOMObserver } from 'observers/domObserver'; import { createDOMObserver, DOMObserver } from 'observers/domObserver';
@@ -54,29 +49,12 @@ type ExcludeFromTuple<T extends readonly any[], E> = T extends [infer F, ...infe
const hostSelector = `[${dataAttributeHost}]`; const hostSelector = `[${dataAttributeHost}]`;
// TODO: observer textarea attrs if textarea // TODO: observer textarea attrs if textarea
// TODO: test _ignoreContentChange & _ignoreNestedTargetChange for content dom observer
// TODO: test _ignoreTargetChange for target dom observer
const viewportSelector = `.${classNameViewport}`; const viewportSelector = `.${classNameViewport}`;
const contentSelector = `.${classNameContent}`;
const ignorePrefix = 'os-';
const viewportAttrsFromTarget = ['tabindex']; const viewportAttrsFromTarget = ['tabindex'];
const baseStyleChangingAttrsTextarea = ['wrap', 'cols', 'rows']; const baseStyleChangingAttrsTextarea = ['wrap', 'cols', 'rows'];
const baseStyleChangingAttrs = ['id', 'class', 'style', 'open']; const baseStyleChangingAttrs = ['id', 'class', 'style', 'open'];
const ignoreTargetChange = (
target: Node,
attrName: string,
oldValue: string | null,
newValue: string | null
) => {
if (attrName === 'class' && oldValue && newValue) {
const diff = diffClass(oldValue, newValue);
return !!diff.find((addedOrRemovedClass) => addedOrRemovedClass.indexOf(ignorePrefix) !== 0);
}
return false;
};
export const createStructureSetupObservers = ( export const createStructureSetupObservers = (
structureSetupElements: StructureSetupElementsObj, structureSetupElements: StructureSetupElementsObj,
state: SetupState<StructureSetupState>, state: SetupState<StructureSetupState>,
@@ -198,21 +176,23 @@ export const createStructureSetupObservers = (
const [destroyHostMutationObserver] = createDOMObserver(_host, false, onHostMutation, { const [destroyHostMutationObserver] = createDOMObserver(_host, false, onHostMutation, {
_styleChangingAttributes: baseStyleChangingAttrs, _styleChangingAttributes: baseStyleChangingAttrs,
_attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget), _attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget),
_ignoreTargetChange: ignoreTargetChange,
}); });
updateViewportAttrsFromHost(); updateViewportAttrsFromHost();
return [ return [
(checkOption) => { (checkOption) => {
const [ignoreMutation] = checkOption<string[] | null>('updating.ignoreMutation');
const [attributes, attributesChanged] = checkOption<string[] | null>('updating.attributes');
const [elementEvents, elementEventsChanged] = checkOption<Array<[string, string]> | null>( const [elementEvents, elementEventsChanged] = checkOption<Array<[string, string]> | null>(
'updating.elementEvents' 'updating.elementEvents'
); );
const [attributes, attributesChanged] = checkOption<string[] | null>('updating.attributes');
const [debounceValue, debounceChanged] = checkOption<Array<number> | number | null>( const [debounceValue, debounceChanged] = checkOption<Array<number> | number | null>(
'updating.debounce' 'updating.debounce'
); );
const updateContentMutationObserver = elementEventsChanged || attributesChanged; const updateContentMutationObserver = elementEventsChanged || attributesChanged;
const ignoreMutationFromOptions = (mutation: MutationRecord) =>
isFunction(ignoreMutation) && ignoreMutation(mutation);
if (updateContentMutationObserver) { if (updateContentMutationObserver) {
if (contentMutationObserver) { if (contentMutationObserver) {
@@ -227,17 +207,14 @@ export const createStructureSetupObservers = (
_styleChangingAttributes: contentMutationObserverAttr.concat(attributes || []), _styleChangingAttributes: contentMutationObserverAttr.concat(attributes || []),
_attributes: contentMutationObserverAttr.concat(attributes || []), _attributes: contentMutationObserverAttr.concat(attributes || []),
_eventContentChange: elementEvents, _eventContentChange: elementEvents,
_ignoreNestedTargetChange: ignoreTargetChange,
_nestedTargetSelector: hostSelector, _nestedTargetSelector: hostSelector,
_ignoreContentChange: (mutation, isNestedTarget) => { _ignoreContentChange: (mutation, isNestedTarget) => {
const { target, attributeName } = mutation; const { target, attributeName } = mutation;
return !isNestedTarget && attributeName const ignore =
? liesBetween( !isNestedTarget && attributeName
target as Element, ? liesBetween(target as Element, hostSelector, viewportSelector)
hostSelector, : false;
_content ? contentSelector : viewportSelector return ignore || !!ignoreMutationFromOptions(mutation);
)
: false;
}, },
} }
); );
@@ -137,12 +137,6 @@ const targetDomObserver = createDOMObserver(
should.ok(false, 'A target dom observer must not call the _ignoreContentChange method.'); should.ok(false, 'A target dom observer must not call the _ignoreContentChange method.');
return true; return true;
}, },
// @ts-ignore
_ignoreNestedTargetChange: () => {
// if param: isContentObserver = false, this function should never be called.
should.ok(false, 'A target dom observer must not call the _ignoreNestedTargetChange method.');
return true;
},
} }
); );
@@ -182,7 +176,7 @@ const createContentDomOserver = (
? liesBetween(target as Element, hostSelector, '.content') ? liesBetween(target as Element, hostSelector, '.content')
: false; : false;
}, },
_ignoreNestedTargetChange: (_target, attrName, oldValue, newValue) => { _ignoreTargetChange: (_target, attrName, oldValue, newValue) => {
if (attrName === 'class' && oldValue && newValue) { if (attrName === 'class' && oldValue && newValue) {
const diff = diffClass(oldValue, newValue); const diff = diffClass(oldValue, newValue);
const ignore = diff.length === 1 && diff[0].startsWith(ignorePrefix); const ignore = diff.length === 1 && diff[0].startsWith(ignorePrefix);
@@ -190,12 +184,6 @@ const createContentDomOserver = (
} }
return false; return false;
}, },
// @ts-ignore
_ignoreTargetChange: () => {
// if param: isContentObserver = true, this function should never be called.
should.ok(false, 'A content dom observer must not call the _ignoreTargetChange method.');
return true;
},
} }
); );
@@ -503,7 +491,8 @@ const addRemoveImgElmsFn = async (changeless = false) => {
const addChanged = async ( const addChanged = async (
newEventContentChange: Array<[string?, string?] | null | undefined> newEventContentChange: Array<[string?, string?] | null | undefined>
) => { ) => {
contentDomObserver._destroy(); const [destroyA] = contentDomObserver;
destroyA();
contentDomObserver = createContentDomOserver(newEventContentChange); contentDomObserver = createContentDomOserver(newEventContentChange);
const img = new Image(1, 1); const img = new Image(1, 1);
@@ -523,7 +512,8 @@ const addRemoveImgElmsFn = async (changeless = false) => {
compare(1); compare(1);
}); });
contentDomObserver._destroy(); const [destroyB] = contentDomObserver;
destroyB();
contentDomObserver = createContentDomOserver(contentChange); contentDomObserver = createContentDomOserver(contentChange);
}; };
@@ -604,7 +594,8 @@ const addRemoveTransitionElmsFn = async () => {
}); });
await startTransition(elm, expectTransitionEndContentChange && true); await startTransition(elm, expectTransitionEndContentChange && true);
contentDomObserver._destroy(); const [destroy] = contentDomObserver;
destroy();
contentDomObserver = createContentDomOserver(contentChange); contentDomObserver = createContentDomOserver(contentChange);
await startTransition(elm, expectTransitionEndContentChange && false); await startTransition(elm, expectTransitionEndContentChange && false);
@@ -615,7 +606,8 @@ const addRemoveTransitionElmsFn = async () => {
await add(false); await add(false);
contentDomObserver._destroy(); const [destroy] = contentDomObserver;
destroy();
contentDomObserver = createContentDomOserver( contentDomObserver = createContentDomOserver(
contentChange.concat([['.transition', 'transitionend']]) contentChange.concat([['.transition', 'transitionend']])
); );
@@ -734,15 +726,17 @@ const start = async () => {
await addRemoveImgElmsFn(); await addRemoveImgElmsFn();
targetDomObserver._update(); const [destroyTarget, updateTarget] = targetDomObserver;
targetDomObserver._destroy(); updateTarget();
targetDomObserver._destroy(); destroyTarget();
targetDomObserver._update(); destroyTarget();
updateTarget();
contentDomObserver._update(); const [destroyContent, updateContent] = contentDomObserver;
contentDomObserver._destroy(); updateContent();
contentDomObserver._destroy(); destroyContent();
contentDomObserver._update(); destroyContent();
updateContent();
await addRemoveImgElmsFn(true); // won't trigger changes after destroy await addRemoveImgElmsFn(true); // won't trigger changes after destroy
@@ -5,7 +5,7 @@ import { OverlayScrollbars } from 'overlayscrollbars';
import { resize } from '@/testing-browser/Resize'; import { resize } from '@/testing-browser/Resize';
import { timeout } from '@/testing-browser/timeout'; import { timeout } from '@/testing-browser/timeout';
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult'; import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { addClass, removeAttr, style } from 'support'; import { addClass, each, isArray, removeAttr, style } from 'support';
OverlayScrollbars.env().setDefaultOptions({ OverlayScrollbars.env().setDefaultOptions({
nativeScrollbarsOverlaid: { initialize: true }, nativeScrollbarsOverlaid: { initialize: true },
@@ -28,14 +28,16 @@ const resizeBetweenB: HTMLElement | null = document.createElement('div');
let rootUpdateCount = 0; let rootUpdateCount = 0;
let aUpdateCount = 0; let aUpdateCount = 0;
let bUpdateCount = 0; let bUpdateCount = 0;
OverlayScrollbars( const rootInstance = OverlayScrollbars(
targetRoot!, { target: targetRoot!, padding: true },
{}, {},
{ {
initialized() { initialized() {
addClass(targetRoot!.querySelector('.os-viewport'), 'flex'); requestAnimationFrame(() => {
addClass(resizeBetweenRoot, 'resize resizeBetween'); addClass(rootInstance.elements().content, 'flex');
targetRoot!.append(resizeBetweenRoot); addClass(resizeBetweenRoot, 'resize resizeBetween');
targetRoot!.append(resizeBetweenRoot);
});
}, },
updated() { updated() {
rootUpdateCount++; rootUpdateCount++;
@@ -47,17 +49,18 @@ OverlayScrollbars(
}, },
} }
); );
OverlayScrollbars( const aInstance = OverlayScrollbars(
{ target: targetA!, content: true }, { target: targetA!, content: true },
{}, {},
{ {
initialized() { initialized() {
addClass(targetA!.querySelector('.os-content'), 'flex'); requestAnimationFrame(() => {
addClass(resizeBetweenA, 'resize resizeBetween'); addClass(aInstance.elements().content, 'flex');
targetA!.append(resizeBetweenA); addClass(resizeBetweenA, 'resize resizeBetween');
targetA!.append(resizeBetweenA);
});
}, },
updated(args) { updated() {
console.log(args);
aUpdateCount++; aUpdateCount++;
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (updatesASlot) { if (updatesASlot) {
@@ -67,7 +70,7 @@ OverlayScrollbars(
}, },
} }
); );
OverlayScrollbars( const bInstance = OverlayScrollbars(
targetB!, targetB!,
{}, {},
{ {
@@ -131,7 +134,26 @@ const resizeResize = async (resizeElm: HTMLElement) => {
removeAttr(resizeElm, 'style'); removeAttr(resizeElm, 'style');
}; };
const overwriteScrollHeight = (elm: HTMLElement | HTMLElement[]) => {
const elements = isArray(elm) ? elm : [elm];
each(elements, (currElm) => {
Object.defineProperty(currElm, 'scrollHeight', {
configurable: true,
get() {
setTestResult(false);
throw new Error('accessed scrollHeight');
},
});
});
};
const testBetweenElements = async () => { const testBetweenElements = async () => {
overwriteScrollHeight([
rootInstance.elements().viewport,
aInstance.elements().viewport,
bInstance.elements().viewport,
]);
await waitForOrFailTest(async () => { await waitForOrFailTest(async () => {
await resizeBetween(resizeBetweenRoot); await resizeBetween(resizeBetweenRoot);
await resizeBetween(resizeBetweenA); await resizeBetween(resizeBetweenA);
@@ -150,8 +172,8 @@ const testResizeElements = async () => {
const start = async () => { const start = async () => {
setTestResult(null); setTestResult(null);
await testBetweenElements();
await testResizeElements(); await testResizeElements();
await testBetweenElements(); // has to be last
setTestResult(true); setTestResult(true);
}; };
@@ -0,0 +1,47 @@
import './index.scss';
import 'styles/overlayscrollbars.scss';
import should from 'should';
import { OverlayScrollbars } from 'overlayscrollbars';
import { resize } from '@/testing-browser/Resize';
import { timeout } from '@/testing-browser/timeout';
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { addClass, each, isArray, removeAttr, style } from 'support';
OverlayScrollbars.env().setDefaultOptions({
nativeScrollbarsOverlaid: { initialize: true },
});
const startBtn: HTMLButtonElement | null = document.querySelector('#start');
const target: HTMLElement | null = document.querySelector('#target');
const updatesSlot: HTMLElement | null = document.querySelector('#update');
let updateCount = 0;
const osInstance = OverlayScrollbars(
{ target: target! },
{
updating: {
ignoreMutation(mutation) {
console.log(mutation);
},
},
},
{
updated() {
updateCount++;
requestAnimationFrame(() => {
if (updatesSlot) {
updatesSlot.textContent = `${updateCount}`;
}
});
},
}
);
const start = async () => {
setTestResult(null);
setTestResult(true);
};
startBtn?.addEventListener('click', start);
@@ -0,0 +1,10 @@
<div id="controls">
<button id="start">start</button>
</div>
<div id="stage">
<div>
<div id="target" class="container">
<span>Hello</span>
</div>
</div>
</div>
@@ -0,0 +1,57 @@
body {
display: flex;
flex-direction: column;
overflow: scroll;
}
#controls {
flex: none;
}
#stage {
flex: auto;
position: relative;
& > div {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: lightgoldenrodyellow;
}
}
.container {
border: 1px solid red;
width: 60%;
height: 60%;
padding: 10px;
margin: 10px;
}
.resize {
overflow: hidden;
background: lime;
border: 1px solid green;
padding: 10px;
}
.resizer {
position: relative;
}
.resizeBetween {
background: tomato;
position: absolute;
bottom: 0;
left: 0;
}
.resizeBtn {
position: absolute;
bottom: 0;
right: 0;
height: 20px;
width: 20px;
background: blue;
opacity: 0.3;
}
@@ -0,0 +1,12 @@
// @ts-ignore
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
import { test } from '@playwright/test';
playwrightRollup();
test.describe('StructureSetup.elements', () => {
test('nesting updates', async ({ page }) => {
await page.click('#start');
await expectSuccess(page);
});
});
@@ -202,7 +202,7 @@ const metricsDimensionsEqual = (a: Metrics, b: Metrics) => {
return JSON.stringify(aDimensions) === JSON.stringify(bDimensions); return JSON.stringify(aDimensions) === JSON.stringify(bDimensions);
}; };
target!.querySelector('.os-viewport')?.addEventListener('scroll', (e) => { osInstance.elements().viewport.addEventListener('scroll', (e) => {
const viewport: HTMLElement | null = e.currentTarget as HTMLElement; const viewport: HTMLElement | null = e.currentTarget as HTMLElement;
comparison!.scrollLeft = viewport.scrollLeft; comparison!.scrollLeft = viewport.scrollLeft;
comparison!.scrollTop = viewport.scrollTop; comparison!.scrollTop = viewport.scrollTop;
@@ -265,15 +265,13 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
await waitForOrFailTest(async () => { await waitForOrFailTest(async () => {
const comparisonMetrics = getMetrics(comparison!); const comparisonMetrics = getMetrics(comparison!);
const targetMetrics = getMetrics(target!); const targetMetrics = getMetrics(target!);
const targetViewport = target!.querySelector<HTMLElement>('.os-viewport'); const targetViewport = osInstance.elements().viewport;
const targetPadding = target!.querySelector<HTMLElement>('.os-padding'); const targetPadding = osInstance.elements().padding;
const { x: overflowOptionX, y: overflowOptionY } = osInstance.options().overflow; const { x: overflowOptionX, y: overflowOptionY } = osInstance.options().overflow;
const overflowOptionXVisible = isVisibleOverflow(overflowOptionX); const overflowOptionXVisible = isVisibleOverflow(overflowOptionX);
const overflowOptionYVisible = isVisibleOverflow(overflowOptionY); const overflowOptionYVisible = isVisibleOverflow(overflowOptionY);
const hostOverflowStyle = style(target, 'overflow'); const hostOverflowStyle = style(target, 'overflow');
const paddingOverflowStyle = targetPadding const paddingOverflowStyle = style(targetPadding, 'overflow');
? style(targetPadding, 'overflow')
: hostOverflowStyle;
const viewportOverflowXStyle = style(targetViewport!, 'overflowX'); const viewportOverflowXStyle = style(targetViewport!, 'overflowX');
const viewportOverflowYStyle = style(targetViewport!, 'overflowY'); const viewportOverflowYStyle = style(targetViewport!, 'overflowY');