Won't trigger DOM Change
+DOM Content Change shouldn't be triggered
+diff --git a/config/jest-puppeteer.rollup.js b/config/jest-puppeteer.rollup.js index d0b1ebd..cd7e313 100644 --- a/config/jest-puppeteer.rollup.js +++ b/config/jest-puppeteer.rollup.js @@ -15,6 +15,18 @@ const cacheFilePrefix = 'jest-puppeteer-overlayscrollbars-cache-'; const cacheEncoding = 'utf8'; const cacheHash = 'md5'; +const rollupAdditionalWatchFiles = (files) => ({ + buildStart() { + if (files) { + files.forEach((file) => { + if (fs.existsSync(file)) { + this.addWatchFile(file); + } + }); + } + }, +}); + const makeHtmlAttributes = (attributes) => { if (!attributes) { return ''; @@ -26,7 +38,7 @@ const makeHtmlAttributes = (attributes) => { return keys.reduce((result, key) => (result += ` ${key}="${attributes[key]}"`), ''); }; -const genHtmlTemplateFunc = (content) => ({ attributes, files, meta, publicPath, title }) => { +const genHtmlTemplateFunc = (contentOrContentFn) => ({ attributes, files, meta, publicPath, title }) => { const scripts = (files.js || []) .map(({ fileName }) => ``) .join('\n'); @@ -87,7 +99,7 @@ const genHtmlTemplateFunc = (content) => ({ attributes, files, meta, publicPath, ${links}
- ${content || ''} + ${(typeof contentOrContentFn === 'function' ? contentOrContentFn() : contentOrContentFn) || ''} ${scripts} @@ -152,8 +164,19 @@ const setupRollupTest = async (rootDir, testPath, cacheDir, watch) => { if (typeof rollupConfig === 'function') { try { const htmlFilePath = path.resolve(testDir, deploymentConfig.html.input); - const htmlFileContent = fs.existsSync(htmlFilePath) ? fs.readFileSync(htmlFilePath, 'utf8') : null; const dist = path.resolve(testDir, deploymentConfig.build); + const getHtmlFileContent = () => (fs.existsSync(htmlFilePath) ? fs.readFileSync(htmlFilePath, 'utf8') : null); + const logBuilding = (re) => { + const text = re ? ' RE-BUILDING ' : ' BUILDING '; + console.log(`\x1b[1m\x1b[44m${text}\x1b[0m \x1b[90m${testPath}\x1b[0m`); // eslint-disable-line + }; + const logBundleFinish = (duration) => { + if (duration) { + console.log(`Bundle finished after ${Math.round(duration / 1000)} seconds.`); // eslint-disable-line + } else { + console.log(`Bundle finished.`); // eslint-disable-line + } + }; let rollupConfigObj = rollupConfig(undefined, { project: rootDir, @@ -173,11 +196,12 @@ const setupRollupTest = async (rootDir, testPath, cacheDir, watch) => { rollupPluginHtml({ title: `Jest-Puppeteer: ${testName}`, fileName: deploymentConfig.html.output, - template: genHtmlTemplateFunc(htmlFileContent), + template: genHtmlTemplateFunc(getHtmlFileContent), meta: [{ charset: 'utf-8' }, { 'http-equiv': 'X-UA-Compatible', content: 'IE=edge' }], }), ...(watch ? [ + rollupAdditionalWatchFiles([htmlFilePath]), rollupPluginServe({ contentBase: dist, historyApiFallback: `/${deploymentConfig.html.output}`, @@ -225,10 +249,13 @@ const setupRollupTest = async (rootDir, testPath, cacheDir, watch) => { console.log('Error:', error); // eslint-disable-line } if (code === 'START') { - console.log(firstWatch ? 'Building...' : 'Rebuilding...'); // eslint-disable-line + if (firstWatch) { + console.log(''); // eslint-disable-line + } + logBuilding(!firstWatch); } if (code === 'BUNDLE_END') { - console.log(`Bundle finished after ${Math.round(duration / 1000)} seconds.`); // eslint-disable-line + logBundleFinish(duration); if (result && result.close) { result.close(); } @@ -246,6 +273,9 @@ const setupRollupTest = async (rootDir, testPath, cacheDir, watch) => { rollupWatchers.push(rollupWatcher); } else { + console.log(''); // eslint-disable-line + logBuilding(); + const startTime = Date.now(); // eslint-disable-next-line no-await-in-loop const bundle = await rollup.rollup(inputConfig); @@ -253,7 +283,12 @@ const setupRollupTest = async (rootDir, testPath, cacheDir, watch) => { const outputConfig = output[i]; // eslint-disable-next-line no-await-in-loop await bundle.write(outputConfig); + + const endTime = Date.now(); + logBundleFinish(endTime - startTime); } + + console.log(''); // eslint-disable-line } } diff --git a/packages/overlayscrollbars/src/observers/domObserver.ts b/packages/overlayscrollbars/src/observers/domObserver.ts index 748de7b..5f971de 100644 --- a/packages/overlayscrollbars/src/observers/domObserver.ts +++ b/packages/overlayscrollbars/src/observers/domObserver.ts @@ -1,9 +1,19 @@ -import { each, indexOf, isString, MutationObserverConstructor, isEmptyArray, liesBetween } from 'support'; -import { classNameHost, classNameContent } from 'classnames'; +import { each, indexOf, isString, MutationObserverConstructor, isEmptyArray, on, off, attr, is, find } from 'support'; +type StringNullUndefined = string | null | undefined; + +export type DOMOvserverEventContentChangeResult = Array<[StringNullUndefined, StringNullUndefined] | null | undefined>; // [selector, eventname] +export type DOMOvserverEventContentChange = () => DOMOvserverEventContentChangeResult; +export type DOMObserverIgnoreContentChange = ( + mutation: MutationRecord, + domObserverTarget: HTMLElement, + domObserverOptions: DOMObserverOptions | undefined +) => boolean | null | undefined; export interface DOMObserverOptions { _observeContent?: boolean; _attributes?: string[]; + _ignoreContentChange?: DOMObserverIgnoreContentChange; + _eventContentChange?: DOMOvserverEventContentChange; } export interface DOMObserver { _disconnect: () => void; @@ -12,76 +22,115 @@ export interface DOMObserver { const styleChangingAttributes = ['id', 'class', 'style', 'open']; const mutationObserverAttrsTextarea = ['wrap', 'cols', 'rows']; - -const isUnknownMutation = ( - attributeName: string | null, - type: MutationRecordType, - observeContent?: boolean, - target?: Node, - mutationTarget?: Node -) => { - const isAttributesType = type === 'attributes'; - const targetIsMutationTarget = target === mutationTarget; - const styleChangingAttrChanged = indexOf(styleChangingAttributes, attributeName) > -1; - const contentChanged = observeContent && !isAttributesType; - const contentAttrChanged = - observeContent && - isAttributesType && - styleChangingAttrChanged && - !targetIsMutationTarget && - !liesBetween(mutationTarget as Element | undefined, `.${classNameHost}`, `.${classNameContent}`); - const targetAttrChanged = isAttributesType && styleChangingAttrChanged && targetIsMutationTarget && !observeContent; - - return contentChanged || contentAttrChanged || targetAttrChanged; -}; +const getAttributeChanged = (mutationTarget: Node, attributeName: string, oldValue: string | null): boolean => + oldValue !== attr(mutationTarget as HTMLElement, attributeName); export const createDOMObserver = ( target: HTMLElement, - callback: (changedTargetAttrs: string[], styleChanged: boolean, contentChanged: boolean) => any, + callback: (targetChangedAttrs: string[], targetStyleChanged: boolean, contentChanged: boolean) => any, options?: DOMObserverOptions ): DOMObserver => { - const { _observeContent, _attributes } = options || {}; + let isConnected = false; + const { _observeContent, _attributes, _ignoreContentChange, _eventContentChange } = options || {}; + const eventContentChangeCallback = () => { + if (isConnected) { + callback([], false, true); + } + }; + const refreshEventContentChange = (getElements: (selector: string) => Node[]) => { + if (_eventContentChange) { + const eventContentChanges = _eventContentChange(); + const eventElmList = eventContentChanges.reduce