mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-21 21:50:36 +03:00
switch to playwright test runner, use esbuild for dev builds, switch to array destructuring
This commit is contained in:
+17
-16
@@ -1,8 +1,12 @@
|
|||||||
const resolve = require('./resolve.config');
|
const resolve = require('./resolve.config');
|
||||||
const browserRollupConfig = require('./config/jest-puppeteer.rollup.config.js');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ['plugin:jest-playwright/recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react/recommended', 'airbnb', 'prettier'],
|
extends: [
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react/recommended',
|
||||||
|
'airbnb',
|
||||||
|
'prettier',
|
||||||
|
],
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es2020: true,
|
es2020: true,
|
||||||
@@ -43,6 +47,13 @@ module.exports = {
|
|||||||
'consistent-return': 'off',
|
'consistent-return': 'off',
|
||||||
'import/prefer-default-export': 'off',
|
'import/prefer-default-export': 'off',
|
||||||
'import/no-extraneous-dependencies': 'off',
|
'import/no-extraneous-dependencies': 'off',
|
||||||
|
'import/no-unresolved': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
ignore: [`^@/.*`],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
@@ -54,12 +65,6 @@ module.exports = {
|
|||||||
allowedNames: ['self', '_self'], // Allow `const self = this`; `[]` by default
|
allowedNames: ['self', '_self'], // Allow `const self = this`; `[]` by default
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'import/no-unresolved': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
ignore: [`^@/.*`],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/extensions': [
|
'import/extensions': [
|
||||||
'error',
|
'error',
|
||||||
'ignorePackages',
|
'ignorePackages',
|
||||||
@@ -73,8 +78,10 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ['*.test.*', `*${browserRollupConfig.js.input}.*`],
|
files: ['*.test.*', '**/tests/**'],
|
||||||
rules: {
|
rules: {
|
||||||
|
'no-shadow': 'off',
|
||||||
|
'no-use-before-define': 'off',
|
||||||
'no-restricted-syntax': 'off',
|
'no-restricted-syntax': 'off',
|
||||||
'@typescript-eslint/ban-ts-comment': 'off',
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
'@typescript-eslint/no-empty-function': 'off',
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
@@ -87,16 +94,10 @@ module.exports = {
|
|||||||
'no-void': 'off',
|
'no-void': 'off',
|
||||||
'no-empty-function': 'off',
|
'no-empty-function': 'off',
|
||||||
'no-new-func': 'off',
|
'no-new-func': 'off',
|
||||||
'import/no-unresolved': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
ignore: [`\\./${browserRollupConfig.build}/${browserRollupConfig.html.output}$`, `^@/.*`],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ['rollup.config.*'],
|
files: ['*rollup*'],
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
'global-require': 'off',
|
'global-require': 'off',
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"printWidth": 150,
|
"printWidth": 100,
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
|
|||||||
+4
-12
@@ -1,21 +1,13 @@
|
|||||||
module.exports = function (api) {
|
module.exports = function (api) {
|
||||||
api.cache.using(() => process.env.NODE_ENV);
|
api.cache.using(() => process.env.NODE_ENV);
|
||||||
const isRollup = api.caller((caller) => !!(caller && caller.name === 'babel-rollup-build'));
|
|
||||||
const isJest = api.caller((caller) => !!(caller && caller.name === 'babel-jest'));
|
const isJest = api.caller((caller) => !!(caller && caller.name === 'babel-jest'));
|
||||||
|
|
||||||
if (isRollup) {
|
|
||||||
return {
|
|
||||||
plugins: [
|
|
||||||
'@babel/plugin-transform-runtime',
|
|
||||||
'@babel/plugin-proposal-class-properties',
|
|
||||||
['@babel/plugin-proposal-private-methods', { loose: false }],
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isJest) {
|
if (isJest) {
|
||||||
return {
|
return {
|
||||||
plugins: ['@babel/plugin-transform-modules-commonjs', ['@babel/plugin-proposal-private-methods', { loose: false }]],
|
plugins: [
|
||||||
|
'@babel/plugin-transform-modules-commonjs',
|
||||||
|
['@babel/plugin-proposal-private-methods', { loose: false }],
|
||||||
|
],
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
'@babel/preset-env',
|
'@babel/preset-env',
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
const PlaywrightEnvironment = require('jest-playwright-preset/lib/PlaywrightEnvironment').default;
|
|
||||||
const { setupRollupTest, cleanupRollupTest } = require('./jest-browser.rollup.js');
|
|
||||||
|
|
||||||
const buildTests = [];
|
|
||||||
|
|
||||||
class BrowserRollupEnvironment extends PlaywrightEnvironment {
|
|
||||||
constructor(envConfig, envContext) {
|
|
||||||
super(envConfig, envContext);
|
|
||||||
|
|
||||||
this.watch = (envConfig.displayName.name || '').includes('-dev');
|
|
||||||
this.ctx = envContext;
|
|
||||||
this.cfg = envConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setup() {
|
|
||||||
const { testPath } = this.ctx;
|
|
||||||
|
|
||||||
if (!buildTests.includes(testPath)) {
|
|
||||||
await cleanupRollupTest(testPath, this.cfg.cache);
|
|
||||||
await setupRollupTest(this.cfg.rootDir, this.ctx.testPath, this.cfg.cache && this.cfg.cacheDirectory, this.watch);
|
|
||||||
buildTests.push(testPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
await super.setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
async teardown() {
|
|
||||||
await super.teardown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = BrowserRollupEnvironment;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const { globalSetup } = require('jest-playwright-preset');
|
|
||||||
|
|
||||||
module.exports = async (jestConfig) => {
|
|
||||||
await globalSetup(jestConfig);
|
|
||||||
};
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const { execSync } = require('child_process');
|
|
||||||
const del = require('del');
|
|
||||||
const { globalTeardown } = require('jest-playwright-preset');
|
|
||||||
|
|
||||||
const coverageTempDir = './.nyc_output';
|
|
||||||
const coverageTempDirFile = 'coverage.json';
|
|
||||||
const reportDir = './.coverage/browser';
|
|
||||||
|
|
||||||
module.exports = async (jestConfig) => {
|
|
||||||
await globalTeardown(jestConfig);
|
|
||||||
|
|
||||||
const { rootDir } = jestConfig;
|
|
||||||
const coverageTempDirPath = path.resolve(rootDir, coverageTempDir);
|
|
||||||
const coverageTempFilePath = path.resolve(coverageTempDirPath, coverageTempDirFile);
|
|
||||||
const reportDirPath = path.resolve(rootDir, reportDir);
|
|
||||||
|
|
||||||
if (fs.existsSync(coverageTempFilePath)) {
|
|
||||||
const coverageReportText = ' COVERAGE ';
|
|
||||||
|
|
||||||
console.log('');
|
|
||||||
console.log(`\x1b[1m\x1b[44m${coverageReportText}\x1b[0m`);
|
|
||||||
console.log(`Reporting from: "${path.relative(rootDir, coverageTempFilePath)}" in "${path.relative(rootDir, reportDirPath)}"`);
|
|
||||||
|
|
||||||
del.sync(reportDirPath);
|
|
||||||
execSync(`npx nyc report --reporter=lcov --report-dir=${reportDir}`, {
|
|
||||||
cwd: rootDir,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [deletedTempDir] = del.sync(coverageTempDirPath);
|
|
||||||
if (deletedTempDir) {
|
|
||||||
console.log('Deleted:', path.relative(rootDir, deletedTempDir));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
port: 8080,
|
|
||||||
root: path.join(__dirname, '../'),
|
|
||||||
build: '.build',
|
|
||||||
html: {
|
|
||||||
input: 'index.html',
|
|
||||||
output: 'build.html',
|
|
||||||
},
|
|
||||||
js: {
|
|
||||||
input: 'index.browser',
|
|
||||||
output: 'build',
|
|
||||||
},
|
|
||||||
dev: {
|
|
||||||
servePort: 18080,
|
|
||||||
livereloadPort: 28080,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const path = require('path');
|
|
||||||
const del = require('del');
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const readline = require('readline');
|
|
||||||
const rollup = require('rollup');
|
|
||||||
const rollupPluginHtml = require('@rollup/plugin-html');
|
|
||||||
const rollupPluginStyles = require('rollup-plugin-styles');
|
|
||||||
const rollupPluginServe = require('rollup-plugin-serve');
|
|
||||||
const rollupPluginLivereload = require('rollup-plugin-livereload');
|
|
||||||
const deploymentConfig = require('./jest-browser.rollup.config.js');
|
|
||||||
|
|
||||||
const rollupConfigName = 'rollup.config.js';
|
|
||||||
const cacheFilePrefix = 'jest-browser-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 '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys = Object.keys(attributes);
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
// eslint-disable-next-line no-return-assign
|
|
||||||
return keys.reduce((result, key) => (result += ` ${key}="${attributes[key]}"`), '');
|
|
||||||
};
|
|
||||||
|
|
||||||
const genHtmlTemplateFunc = (contentOrContentFn) => ({ attributes, files, meta, publicPath, title }) => {
|
|
||||||
const scripts = (files.js || [])
|
|
||||||
.map(({ fileName }) => `<script src="${publicPath}${fileName}"${makeHtmlAttributes(attributes.script)}></script>`)
|
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
const links = (files.css || [])
|
|
||||||
.map(({ fileName }) => `<link href="${publicPath}${fileName}" rel="stylesheet"${makeHtmlAttributes(attributes.link)}>`)
|
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
const metas = meta.map((input) => `<meta${makeHtmlAttributes(input)}>`).join('\n');
|
|
||||||
|
|
||||||
return `<!doctype html>
|
|
||||||
<html${makeHtmlAttributes(attributes.html)}>
|
|
||||||
<head>
|
|
||||||
${metas}
|
|
||||||
<title>${title}</title>
|
|
||||||
<style>
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
*::before,
|
|
||||||
*::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
* {
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
#testResult {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 5px;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
#testResult.passed {
|
|
||||||
display: block;
|
|
||||||
background: lime;
|
|
||||||
}
|
|
||||||
#testResult.passed::before {
|
|
||||||
content: 'passed';
|
|
||||||
}
|
|
||||||
#testResult.failed {
|
|
||||||
display: block;
|
|
||||||
background: red;
|
|
||||||
}
|
|
||||||
#testResult.failed::before {
|
|
||||||
content: 'failed';
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
${links}
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
${(typeof contentOrContentFn === 'function' ? contentOrContentFn() : contentOrContentFn) || ''}
|
|
||||||
${scripts}
|
|
||||||
<div id="testResult"></div>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAllFilesFrom = (dir, except) => {
|
|
||||||
const result = [];
|
|
||||||
fs.readdirSync(dir).forEach((dirOrFile) => {
|
|
||||||
if (!except.includes(dirOrFile)) {
|
|
||||||
const dirOrFileResolved = path.resolve(dir, dirOrFile);
|
|
||||||
if (fs.statSync(dirOrFileResolved).isDirectory()) {
|
|
||||||
result.push(...getAllFilesFrom(dirOrFileResolved));
|
|
||||||
}
|
|
||||||
result.push(dirOrFileResolved);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const createCacheObj = (testPath) => {
|
|
||||||
const testFileName = path.basename(testPath);
|
|
||||||
const testFiles = getAllFilesFrom(path.dirname(testPath), [deploymentConfig.build, testFileName]);
|
|
||||||
const obj = {};
|
|
||||||
|
|
||||||
testFiles.forEach((dir) => {
|
|
||||||
obj[dir] = crypto.createHash(cacheHash).update(fs.readFileSync(dir, cacheEncoding), cacheEncoding).digest('hex');
|
|
||||||
});
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
const filesChanged = (testPath, cacheDir) => {
|
|
||||||
let result = true;
|
|
||||||
const cacheObjString = JSON.stringify(createCacheObj(testPath));
|
|
||||||
const getCacheFile = path.resolve(cacheDir, cacheFilePrefix + crypto.createHash(cacheHash).update(testPath, cacheEncoding).digest('hex'));
|
|
||||||
if (fs.existsSync(getCacheFile)) {
|
|
||||||
result = cacheObjString !== fs.readFileSync(getCacheFile, cacheEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
fs.writeFileSync(getCacheFile, cacheObjString);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setupRollupTest = async (rootDir, testPath, cacheDir, watch) => {
|
|
||||||
const rollupWatchers = [];
|
|
||||||
const rollupServers = [];
|
|
||||||
const testDir = path.dirname(testPath);
|
|
||||||
const testName = path.basename(testDir);
|
|
||||||
const changed = cacheDir && !watch ? filesChanged(testPath, cacheDir) : true;
|
|
||||||
const buildFolderExists = fs.existsSync(path.resolve(testDir, deploymentConfig.build));
|
|
||||||
|
|
||||||
if (changed || !buildFolderExists) {
|
|
||||||
const rollupConfigPath = path.resolve(rootDir, rollupConfigName);
|
|
||||||
|
|
||||||
if (fs.existsSync(rollupConfigPath)) {
|
|
||||||
const rollupConfig = require(rollupConfigPath); // eslint-disable-line
|
|
||||||
|
|
||||||
if (typeof rollupConfig === 'function') {
|
|
||||||
try {
|
|
||||||
const htmlFilePath = path.resolve(testDir, deploymentConfig.html.input);
|
|
||||||
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(`${chalk.bgBlue.bold.whiteBright(text)} ${chalk.blackBright(testPath)}`); // 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,
|
|
||||||
overwrite: ({ defaultConfig }) => {
|
|
||||||
return {
|
|
||||||
dist,
|
|
||||||
input: path.resolve(testDir, deploymentConfig.js.input),
|
|
||||||
file: deploymentConfig.js.output,
|
|
||||||
types: null,
|
|
||||||
minVersions: false,
|
|
||||||
esmBuild: false,
|
|
||||||
sourcemap: true,
|
|
||||||
name: testName,
|
|
||||||
pipeline: [
|
|
||||||
rollupPluginStyles(),
|
|
||||||
...defaultConfig.pipeline,
|
|
||||||
rollupPluginHtml({
|
|
||||||
title: `Jest-Browser: ${testName}`,
|
|
||||||
fileName: deploymentConfig.html.output,
|
|
||||||
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}`,
|
|
||||||
port: deploymentConfig.dev.servePort,
|
|
||||||
onListening(server) {
|
|
||||||
rollupServers.push(server);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
rollupPluginLivereload({
|
|
||||||
watch: dist,
|
|
||||||
port: deploymentConfig.dev.livereloadPort,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
silent: true,
|
|
||||||
fast: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!Array.isArray(rollupConfigObj)) {
|
|
||||||
rollupConfigObj = [rollupConfigObj];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < rollupConfigObj.length; i++) {
|
|
||||||
const inputConfig = rollupConfigObj[i];
|
|
||||||
let { output } = inputConfig;
|
|
||||||
|
|
||||||
if (!Array.isArray(output)) {
|
|
||||||
output = [output];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (watch) {
|
|
||||||
let firstWatch = true;
|
|
||||||
const rollupWatcher = rollup.watch({
|
|
||||||
...inputConfig,
|
|
||||||
output,
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
rollupWatcher.on('event', ({ code, duration, error, result }) => {
|
|
||||||
if (code === 'ERROR') {
|
|
||||||
console.log('Error:', error); // eslint-disable-line
|
|
||||||
}
|
|
||||||
if (code === 'START') {
|
|
||||||
if (firstWatch) {
|
|
||||||
console.log(''); // eslint-disable-line
|
|
||||||
}
|
|
||||||
logBuilding(!firstWatch);
|
|
||||||
}
|
|
||||||
if (code === 'BUNDLE_END') {
|
|
||||||
logBundleFinish(duration);
|
|
||||||
if (result && result.close) {
|
|
||||||
result.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (code === 'END') {
|
|
||||||
console.log('Watching for changes, press ENTER to continue.'); // eslint-disable-line
|
|
||||||
console.log(''); // eslint-disable-line
|
|
||||||
if (firstWatch) {
|
|
||||||
firstWatch = false;
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
for (let v = 0; v < output.length; v++) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (watch) {
|
|
||||||
const rl = readline.createInterface({
|
|
||||||
input: process.stdin,
|
|
||||||
output: process.stdout,
|
|
||||||
});
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
rl.on('line', () => {
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
rl.on('close', () => {
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
rl.close();
|
|
||||||
rollupWatchers.forEach((watcher) => {
|
|
||||||
watcher.close();
|
|
||||||
});
|
|
||||||
rollupServers.forEach((server) => {
|
|
||||||
server.close();
|
|
||||||
});
|
|
||||||
if (rollupPluginLivereload && global.PLUGIN_LIVERELOAD && global.PLUGIN_LIVERELOAD.server) {
|
|
||||||
global.PLUGIN_LIVERELOAD.server.close();
|
|
||||||
global.PLUGIN_LIVERELOAD.server = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const cleanupRollupTest = async (testPath, cache) => {
|
|
||||||
if (!cache) {
|
|
||||||
await del(path.resolve(path.dirname(testPath), deploymentConfig.build));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = { setupRollupTest, cleanupRollupTest };
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
jest.setTimeout(60000 * 5);
|
|
||||||
context.setDefaultTimeout(60000 * 5);
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
const express = require('express');
|
|
||||||
const deploymentConfig = require('./jest-browser.rollup.config.js');
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
app.use(express.static(deploymentConfig.root));
|
|
||||||
app.listen(deploymentConfig.port);
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const deploymentConfig = require('./jest-browser.rollup.config.js');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
process: (src, filePath) => {
|
|
||||||
const deploymentPath = path.relative(deploymentConfig.root, filePath);
|
|
||||||
const split = deploymentPath.split(path.sep);
|
|
||||||
return `module.exports = ${JSON.stringify(`http://127.0.0.1:${deploymentConfig.port}/${path.posix.join(...split)}`)}`;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const rollupPluginStyles = require('rollup-plugin-styles');
|
||||||
|
const rollupPluginServe = require('rollup-plugin-serve');
|
||||||
|
const rollupPluginLivereload = require('rollup-plugin-livereload');
|
||||||
|
|
||||||
|
const createRollupConfig = require('../rollup/rollup.config');
|
||||||
|
const rollupPluginHtml = require('./rollup.pluginHtml');
|
||||||
|
const rollupAdditionalWatchFiles = require('./rollup.pluginAdditionalWatchFiles');
|
||||||
|
|
||||||
|
const portRange = {
|
||||||
|
min: 20000,
|
||||||
|
max: 60000,
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
dist: './.build',
|
||||||
|
html: './index.html',
|
||||||
|
input: './index.browser',
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = (testDir, onListening = null) => {
|
||||||
|
const name = path.basename(testDir);
|
||||||
|
const htmlFilePath = path.resolve(testDir, meta.html);
|
||||||
|
const dist = path.resolve(testDir, meta.dist);
|
||||||
|
const htmlName = `${name}.html`;
|
||||||
|
const { min, max } = portRange;
|
||||||
|
const port = Math.floor(Math.random() * (max - min + 1) + min);
|
||||||
|
|
||||||
|
return createRollupConfig({
|
||||||
|
project: name,
|
||||||
|
mode: 'dev',
|
||||||
|
paths: {
|
||||||
|
dist,
|
||||||
|
src: path.resolve(testDir, './'),
|
||||||
|
},
|
||||||
|
versions: {
|
||||||
|
minified: false,
|
||||||
|
module: false,
|
||||||
|
},
|
||||||
|
rollup: {
|
||||||
|
input: path.resolve(testDir, meta.input),
|
||||||
|
context: 'this',
|
||||||
|
moduleContext: () => 'this',
|
||||||
|
output: {
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
rollupPluginStyles(),
|
||||||
|
rollupPluginHtml(`Playwright: ${name}`, htmlName, () =>
|
||||||
|
fs.existsSync(htmlFilePath) ? fs.readFileSync(htmlFilePath, 'utf8') : null
|
||||||
|
),
|
||||||
|
...(onListening
|
||||||
|
? [
|
||||||
|
rollupAdditionalWatchFiles([htmlFilePath]),
|
||||||
|
rollupPluginServe({
|
||||||
|
contentBase: dist,
|
||||||
|
historyApiFallback: `/${htmlName}`,
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port,
|
||||||
|
onListening,
|
||||||
|
}),
|
||||||
|
rollupPluginLivereload({
|
||||||
|
watch: dist,
|
||||||
|
port: port - 1,
|
||||||
|
verbose: false,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
module.exports = (files) => ({
|
||||||
|
buildStart() {
|
||||||
|
if (files) {
|
||||||
|
files.forEach((file) => {
|
||||||
|
if (fs.existsSync(file)) {
|
||||||
|
this.addWatchFile(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
const rollupPluginHtml = require('@rollup/plugin-html');
|
||||||
|
|
||||||
|
const makeHtmlAttributes = (attributes) => {
|
||||||
|
if (!attributes) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const keys = Object.keys(attributes);
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
// eslint-disable-next-line no-return-assign
|
||||||
|
return keys.reduce((result, key) => (result += ` ${key}="${attributes[key]}"`), '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const genHtmlTemplateFunc = (contentOrContentFn) => ({
|
||||||
|
attributes,
|
||||||
|
files,
|
||||||
|
meta,
|
||||||
|
publicPath,
|
||||||
|
title,
|
||||||
|
}) => {
|
||||||
|
const scripts = (files.js || [])
|
||||||
|
.map(
|
||||||
|
({ fileName }) =>
|
||||||
|
`<script src="${publicPath}${fileName}"${makeHtmlAttributes(attributes.script)}></script>`
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
const links = (files.css || [])
|
||||||
|
.map(
|
||||||
|
({ fileName }) =>
|
||||||
|
`<link href="${publicPath}${fileName}" rel="stylesheet"${makeHtmlAttributes(
|
||||||
|
attributes.link
|
||||||
|
)}>`
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
const metas = meta.map((input) => `<meta${makeHtmlAttributes(input)}>`).join('\n');
|
||||||
|
|
||||||
|
return `<!doctype html>
|
||||||
|
<html${makeHtmlAttributes(attributes.html)}>
|
||||||
|
<head>
|
||||||
|
${metas}
|
||||||
|
<title>${title}</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
#testResult {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 5px;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
#testResult.passed {
|
||||||
|
display: block;
|
||||||
|
background: lime;
|
||||||
|
}
|
||||||
|
#testResult.passed::before {
|
||||||
|
content: 'passed';
|
||||||
|
}
|
||||||
|
#testResult.failed {
|
||||||
|
display: block;
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
#testResult.failed::before {
|
||||||
|
content: 'failed';
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
${links}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
${(typeof contentOrContentFn === 'function' ? contentOrContentFn() : contentOrContentFn) || ''}
|
||||||
|
${scripts}
|
||||||
|
<div id="testResult"></div>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = (title, fileName, getHtmlContent) =>
|
||||||
|
rollupPluginHtml({
|
||||||
|
title,
|
||||||
|
fileName,
|
||||||
|
template: genHtmlTemplateFunc(getHtmlContent),
|
||||||
|
meta: [{ charset: 'utf-8' }, { 'http-equiv': 'X-UA-Compatible', content: 'IE=edge' }],
|
||||||
|
});
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
loose: true,
|
||||||
|
bugfixes: true,
|
||||||
|
targets: {
|
||||||
|
esmodules: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
loose: true,
|
||||||
|
targets: {
|
||||||
|
ie: '11',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
module.exports = {
|
||||||
|
project: null,
|
||||||
|
mode: 'build',
|
||||||
|
paths: {
|
||||||
|
src: './src',
|
||||||
|
dist: './dist',
|
||||||
|
types: './types',
|
||||||
|
},
|
||||||
|
versions: {
|
||||||
|
minified: true,
|
||||||
|
module: true,
|
||||||
|
},
|
||||||
|
alias: {},
|
||||||
|
rollup: {
|
||||||
|
input: './src/index',
|
||||||
|
output: {
|
||||||
|
sourcemap: true,
|
||||||
|
exports: 'auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const { babel: rollupBabelInputPlugin } = require('@rollup/plugin-babel');
|
||||||
|
const { terser: rollupTerser } = require('rollup-plugin-terser');
|
||||||
|
const rollupTs = require('rollup-plugin-ts');
|
||||||
|
|
||||||
|
const babelConfigUmd = require('./babel.config.umd');
|
||||||
|
const babelConfigEsm = require('./babel.config.esm');
|
||||||
|
const { rollupCommonjs, rollupResolve, rollupAlias } = require('./pipeline.common.plugins');
|
||||||
|
const { extensions } = require('../../resolve.config.json');
|
||||||
|
|
||||||
|
const createOutputWithMinifiedVersion = (output, esm, buildMinifiedVersion) =>
|
||||||
|
[output].concat(
|
||||||
|
buildMinifiedVersion
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
...output,
|
||||||
|
compact: true,
|
||||||
|
file: output.file.replace('.js', '.min.js'),
|
||||||
|
sourcemap: false,
|
||||||
|
plugins: [
|
||||||
|
...(output.plugins || []),
|
||||||
|
rollupTerser({
|
||||||
|
ecma: esm ? 2015 : 5,
|
||||||
|
safari10: true,
|
||||||
|
mangle: {
|
||||||
|
safari10: true,
|
||||||
|
properties: {
|
||||||
|
regex: /^_/,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
compress: {
|
||||||
|
evaluate: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = (esm, options, declarationFiles = false) => {
|
||||||
|
const { rollup, paths, versions, alias } = options;
|
||||||
|
const { output: rollupOutput, input, plugins = [], ...rollupOptions } = rollup;
|
||||||
|
const { name, file, globals, exports, sourcemap: rawSourcemap, ...outputConfig } = rollupOutput;
|
||||||
|
const { minified: buildMinifiedVersion } = versions;
|
||||||
|
const { src: srcPath, dist: distPath, types: typesPath } = paths;
|
||||||
|
const sourcemap = rawSourcemap;
|
||||||
|
|
||||||
|
const output = createOutputWithMinifiedVersion(
|
||||||
|
{
|
||||||
|
...outputConfig,
|
||||||
|
...(!esm && {
|
||||||
|
name,
|
||||||
|
globals,
|
||||||
|
exports,
|
||||||
|
}),
|
||||||
|
sourcemap,
|
||||||
|
format: esm ? 'esm' : 'umd',
|
||||||
|
generatedCode: esm ? 'es2015' : 'es5',
|
||||||
|
file: path.resolve(distPath, `${file}${esm ? '.esm' : ''}.js`),
|
||||||
|
},
|
||||||
|
esm,
|
||||||
|
buildMinifiedVersion
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
input,
|
||||||
|
output,
|
||||||
|
...rollupOptions,
|
||||||
|
plugins: [
|
||||||
|
rollupAlias(alias),
|
||||||
|
rollupTs({
|
||||||
|
tsconfig: (resolvedConfig) => ({
|
||||||
|
...resolvedConfig,
|
||||||
|
declaration: declarationFiles,
|
||||||
|
declarationDir: typesPath,
|
||||||
|
}),
|
||||||
|
include: ['*.ts+(|x)', '**/*.ts+(|x)'],
|
||||||
|
exclude: ['node_modules', '**/node_modules/*'],
|
||||||
|
}),
|
||||||
|
rollupResolve(srcPath),
|
||||||
|
rollupCommonjs(sourcemap),
|
||||||
|
rollupBabelInputPlugin({
|
||||||
|
...(esm ? babelConfigEsm : babelConfigUmd),
|
||||||
|
assumptions: {
|
||||||
|
iterableIsArray: true,
|
||||||
|
noNewArrows: true,
|
||||||
|
noClassCalls: true,
|
||||||
|
ignoreToPrimitiveHint: true,
|
||||||
|
ignoreFunctionLength: true,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'@babel/plugin-transform-runtime',
|
||||||
|
['@babel/plugin-proposal-class-properties', { loose: true }],
|
||||||
|
['@babel/plugin-proposal-private-methods', { loose: true }],
|
||||||
|
],
|
||||||
|
babelHelpers: 'runtime',
|
||||||
|
shouldPrintComment: () => false,
|
||||||
|
caller: {
|
||||||
|
name: 'babel-rollup-build',
|
||||||
|
},
|
||||||
|
extensions,
|
||||||
|
}),
|
||||||
|
...plugins,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
const { nodeResolve: rollupPluginResolve } = require('@rollup/plugin-node-resolve');
|
||||||
|
const rollupPluginCommonjs = require('@rollup/plugin-commonjs');
|
||||||
|
const rollupPluginAlias = require('@rollup/plugin-alias');
|
||||||
|
const { extensions, directories } = require('../../resolve.config.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
rollupAlias: (aliasEntries) =>
|
||||||
|
rollupPluginAlias({
|
||||||
|
entries: aliasEntries,
|
||||||
|
}),
|
||||||
|
rollupCommonjs: (sourcemap) =>
|
||||||
|
rollupPluginCommonjs({
|
||||||
|
sourceMap: sourcemap,
|
||||||
|
extensions,
|
||||||
|
}),
|
||||||
|
rollupResolve: (srcPath) =>
|
||||||
|
rollupPluginResolve({
|
||||||
|
mainFields: ['browser', 'umd:main', 'module', 'main'],
|
||||||
|
rootDir: srcPath,
|
||||||
|
moduleDirectories: directories,
|
||||||
|
extensions,
|
||||||
|
}),
|
||||||
|
};
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const { default: rollupEsBuild } = require('rollup-plugin-esbuild');
|
||||||
|
const { rollupCommonjs, rollupResolve, rollupAlias } = require('./pipeline.common.plugins');
|
||||||
|
|
||||||
|
module.exports = (options) => {
|
||||||
|
const { rollup, paths, alias } = options;
|
||||||
|
const { output: rollupOutput, input, plugins = [], ...rollupOptions } = rollup;
|
||||||
|
const { file, sourcemap: rawSourcemap, ...outputConfig } = rollupOutput;
|
||||||
|
const { src: srcPath, dist: distPath } = paths;
|
||||||
|
const sourcemap = rawSourcemap;
|
||||||
|
|
||||||
|
const output = {
|
||||||
|
...outputConfig,
|
||||||
|
sourcemap: true,
|
||||||
|
format: 'esm',
|
||||||
|
generatedCode: 'es2015',
|
||||||
|
file: path.resolve(distPath, `${file}.js`),
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
input,
|
||||||
|
output,
|
||||||
|
...rollupOptions,
|
||||||
|
plugins: [
|
||||||
|
rollupAlias(alias),
|
||||||
|
rollupResolve(srcPath),
|
||||||
|
rollupEsBuild({
|
||||||
|
include: /\.[jt]sx?$/,
|
||||||
|
sourceMap: true,
|
||||||
|
target: 'es6',
|
||||||
|
tsconfig: './tsconfig.json',
|
||||||
|
}),
|
||||||
|
rollupCommonjs(sourcemap),
|
||||||
|
...plugins,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const glob = require('glob');
|
||||||
|
const resolve = require('../../resolve.config.json');
|
||||||
|
const pkg = require('../../package.json');
|
||||||
|
|
||||||
|
const defaultOptions = require('./defaultOptions');
|
||||||
|
const pipelineBuild = require('./pipeline.build');
|
||||||
|
const pipelineDev = require('./pipeline.dev');
|
||||||
|
|
||||||
|
const repoRoot = path.resolve(__dirname, '../../');
|
||||||
|
|
||||||
|
const appendExtension = (file) =>
|
||||||
|
path.extname(file) === '' ? file + resolve.extensions.find((ext) => fs.existsSync(path.resolve(`${file}${ext}`))) : file;
|
||||||
|
|
||||||
|
const normalizePath = (pathName) => (pathName ? pathName.split(path.sep).join(path.posix.sep) : pathName);
|
||||||
|
|
||||||
|
const resolvePath = (basePath, pathToResolve, appendExt) => {
|
||||||
|
const result = pathToResolve ? (path.isAbsolute(pathToResolve) ? pathToResolve : path.resolve(basePath, pathToResolve)) : null;
|
||||||
|
return normalizePath(result && appendExt ? appendExtension(result) : result);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getWorkspaceAliases = () =>
|
||||||
|
pkg.workspaces
|
||||||
|
.map((pattern) => glob.sync(pattern, { cwd: repoRoot }))
|
||||||
|
.flat()
|
||||||
|
.reduce((obj, resolvedPath) => {
|
||||||
|
let projTsConfig;
|
||||||
|
const absolutePath = path.resolve(repoRoot, resolvedPath);
|
||||||
|
try {
|
||||||
|
projTsConfig = require(`${path.resolve(repoRoot, resolvedPath)}/tsconfig.json`);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
obj[`@/${path.basename(absolutePath)}`] = `${normalizePath(
|
||||||
|
path.resolve(absolutePath, projTsConfig?.compilerOptions?.baseUrl || defaultOptions.paths.src)
|
||||||
|
)}`;
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const mergeAndResolveOptions = (userOptions) => {
|
||||||
|
const { mode: defaultMode, paths: defaultPaths, versions: defaultVersions, alias: defaultAlias, rollup: defaultRollup } = defaultOptions;
|
||||||
|
const { project, mode: rawMode, paths: rawPaths = {}, versions: rawVersions = {}, alias: rawAlias = {}, rollup: rawRollup = {} } = userOptions;
|
||||||
|
const projectPath = process.cwd();
|
||||||
|
const mergedOptions = {
|
||||||
|
project: project || path.basename(projectPath),
|
||||||
|
mode: rawMode || defaultMode,
|
||||||
|
repoRoot,
|
||||||
|
paths: {
|
||||||
|
...defaultPaths,
|
||||||
|
...rawPaths,
|
||||||
|
},
|
||||||
|
versions: {
|
||||||
|
...defaultVersions,
|
||||||
|
...rawVersions,
|
||||||
|
},
|
||||||
|
alias: {
|
||||||
|
...getWorkspaceAliases(),
|
||||||
|
...defaultAlias,
|
||||||
|
...rawAlias,
|
||||||
|
},
|
||||||
|
rollup: {
|
||||||
|
...defaultRollup,
|
||||||
|
...rawRollup,
|
||||||
|
output: {
|
||||||
|
...defaultRollup.output,
|
||||||
|
...(rawRollup.output || {}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { src, dist, types, tests } = mergedOptions.paths;
|
||||||
|
|
||||||
|
mergedOptions.paths.src = resolvePath(projectPath, src);
|
||||||
|
mergedOptions.paths.dist = resolvePath(projectPath, dist);
|
||||||
|
mergedOptions.paths.types = resolvePath(projectPath, types);
|
||||||
|
mergedOptions.paths.tests = resolvePath(projectPath, tests);
|
||||||
|
|
||||||
|
mergedOptions.rollup.input = resolvePath(projectPath, mergedOptions.rollup.input, true);
|
||||||
|
mergedOptions.rollup.output = {
|
||||||
|
...(mergedOptions.rollup.output || {}),
|
||||||
|
name: mergedOptions.rollup.output?.name || mergedOptions.project,
|
||||||
|
file: mergedOptions.rollup.output?.file || mergedOptions.project.toLocaleLowerCase(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return mergedOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createConfig = (userOptions = {}) => {
|
||||||
|
const options = mergeAndResolveOptions(userOptions);
|
||||||
|
const { project, mode, versions } = options;
|
||||||
|
const { module: buildModuleVersion } = versions;
|
||||||
|
const isBuild = mode === 'build';
|
||||||
|
|
||||||
|
if (isBuild) {
|
||||||
|
console.log('');
|
||||||
|
console.log('PROJECT : ', project);
|
||||||
|
console.log('OPTIONS : ', options);
|
||||||
|
|
||||||
|
const umd = pipelineBuild(false, options, true);
|
||||||
|
const esm = buildModuleVersion ? pipelineBuild(true, options) : null;
|
||||||
|
|
||||||
|
return [umd, esm].filter((build) => !!build);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipelineDev(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = createConfig;
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
|
|
||||||
const deploymentConfig = path.resolve(__dirname, './config/jest-browser.rollup.config.js');
|
|
||||||
const testServerPath = path.resolve(__dirname, './config/jest-test-server.js');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
browsers: ['chromium', 'firefox', 'webkit'],
|
|
||||||
collectCoverage: true,
|
|
||||||
launchType: 'LAUNCH',
|
|
||||||
launchOptions: {
|
|
||||||
headless: false,
|
|
||||||
},
|
|
||||||
serverOptions: {
|
|
||||||
command: `node ${testServerPath}`,
|
|
||||||
port: deploymentConfig.port,
|
|
||||||
launchTimeout: 10000,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
+5
-52
@@ -1,64 +1,17 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const resolve = require('./resolve.config');
|
const resolve = require('./resolve.config');
|
||||||
const browserRollupConfig = require('./config/jest-browser.rollup.config.js');
|
|
||||||
|
|
||||||
const testServerLoaderPath = path.resolve(__dirname, './config/jest-test-server.loader.js');
|
|
||||||
const jsdomSetupFile = path.resolve(__dirname, './config/jest-jsdom.setup.js');
|
|
||||||
const browserGlobalSetupPath = path.resolve(__dirname, './config/jest-browser.globalSetup.js');
|
|
||||||
const browserGlobalTeardownPath = path.resolve(__dirname, './config/jest-browser.globalTeardown.js');
|
|
||||||
const browserTestEnvironmentPath = path.resolve(__dirname, './config/jest-browser.env.js');
|
|
||||||
const browserSetupAfterEnvFile = path.resolve(__dirname, './config/jest-browser.setupAfterEnv.js');
|
|
||||||
|
|
||||||
// For a detailed explanation regarding each configuration property, visit:
|
// For a detailed explanation regarding each configuration property, visit:
|
||||||
// https://jestjs.io/docs/en/configuration.html
|
// https://jestjs.io/docs/en/configuration.html
|
||||||
|
|
||||||
const base = {
|
module.exports = {
|
||||||
clearMocks: true,
|
clearMocks: true,
|
||||||
coverageDirectory: './.coverage/jsdom',
|
coverageDirectory: './.coverage/jest',
|
||||||
testEnvironment: 'jsdom',
|
testEnvironment: 'jsdom',
|
||||||
moduleDirectories: resolve.directories,
|
moduleDirectories: resolve.directories,
|
||||||
moduleFileExtensions: resolve.extensions.map((ext) => ext.replace(/\./, '')),
|
moduleFileExtensions: resolve.extensions.map((ext) => ext.replace(/\./, '')),
|
||||||
testPathIgnorePatterns: ['\\\\node_modules\\\\'],
|
testPathIgnorePatterns: ['\\\\node_modules\\\\'],
|
||||||
};
|
displayName: 'jest',
|
||||||
|
setupFilesAfterEnv: [path.resolve(__dirname, './config/jest/jest.setup.js')],
|
||||||
const browserBase = {
|
testMatch: ['**/tests/jest/**/*.test.[jt]s?(x)'],
|
||||||
...base,
|
|
||||||
collectCoverage: false,
|
|
||||||
preset: 'jest-playwright-preset',
|
|
||||||
globalSetup: browserGlobalSetupPath,
|
|
||||||
globalTeardown: browserGlobalTeardownPath,
|
|
||||||
testEnvironment: browserTestEnvironmentPath,
|
|
||||||
setupFilesAfterEnv: [browserSetupAfterEnvFile],
|
|
||||||
testMatch: ['**/tests/browser/**/*.test.[jt]s?(x)'],
|
|
||||||
coveragePathIgnorePatterns: ['/node_modules/', `/${browserRollupConfig.build}/`],
|
|
||||||
transform: {
|
|
||||||
'^.+\\.[jt]sx?$': 'babel-jest',
|
|
||||||
[`^.+${browserRollupConfig.build}.+${browserRollupConfig.html.output}?$`]: testServerLoaderPath,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
...base,
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
...base,
|
|
||||||
displayName: 'jsdom',
|
|
||||||
setupFilesAfterEnv: [jsdomSetupFile],
|
|
||||||
testMatch: ['**/tests/jsdom/**/*.test.[jt]s?(x)'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...browserBase,
|
|
||||||
displayName: {
|
|
||||||
name: 'browser',
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...browserBase,
|
|
||||||
displayName: {
|
|
||||||
name: 'browser-dev',
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|||||||
+25
-32
@@ -4,66 +4,58 @@
|
|||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.14.6",
|
"@babel/core": "^7.18.2",
|
||||||
"@babel/plugin-transform-runtime": "^7.14.5",
|
"@babel/plugin-transform-runtime": "^7.18.2",
|
||||||
"@babel/preset-env": "^7.14.7",
|
"@babel/preset-env": "^7.18.2",
|
||||||
"@babel/preset-typescript": "^7.14.5",
|
"@babel/preset-typescript": "^7.17.12",
|
||||||
"@rollup/plugin-babel": "^5.3.0",
|
"@babel/runtime": "^7.18.2",
|
||||||
"@rollup/plugin-commonjs": "^17.0.0",
|
"@playwright/test": "^1.22.2",
|
||||||
|
"@rollup/plugin-alias": "^3.1.9",
|
||||||
|
"@rollup/plugin-babel": "^5.3.1",
|
||||||
|
"@rollup/plugin-commonjs": "^22.0.0",
|
||||||
"@rollup/plugin-html": "^0.2.0",
|
"@rollup/plugin-html": "^0.2.0",
|
||||||
"@rollup/plugin-inject": "^4.0.2",
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||||
"@rollup/plugin-node-resolve": "^11.0.1",
|
|
||||||
"@rollup/plugin-typescript": "^5.0.2",
|
|
||||||
"@testing-library/dom": "^7.26.3",
|
"@testing-library/dom": "^7.26.3",
|
||||||
"@types/jest": "^26.0.24",
|
"@types/jest": "^26.0.24",
|
||||||
"@typescript-eslint/eslint-plugin": "^3.7.0",
|
"@typescript-eslint/eslint-plugin": "^3.7.0",
|
||||||
"@typescript-eslint/parser": "^3.7.0",
|
"@typescript-eslint/parser": "^3.7.0",
|
||||||
"babel-jest": "^27.0.6",
|
"babel-jest": "^28.1.1",
|
||||||
"babel-plugin-istanbul": "^6.0.0",
|
|
||||||
"bufferutil": "^4.0.1",
|
"bufferutil": "^4.0.1",
|
||||||
"canvas": "^2.6.1",
|
"canvas": "^2.6.1",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"del": "^5.1.0",
|
"del": "^5.1.0",
|
||||||
|
"esbuild": "^0.14.42",
|
||||||
"eslint": "^7.5.0",
|
"eslint": "^7.5.0",
|
||||||
"eslint-config-airbnb": "^18.2.0",
|
"eslint-config-airbnb": "^18.2.0",
|
||||||
"eslint-config-prettier": "^6.11.0",
|
"eslint-config-prettier": "^6.11.0",
|
||||||
"eslint-plugin-import": "^2.22.0",
|
"eslint-plugin-import": "^2.22.0",
|
||||||
"eslint-plugin-jest": "^24.3.6",
|
"eslint-plugin-jest": "^24.3.6",
|
||||||
"eslint-plugin-jest-playwright": "^0.4.1",
|
|
||||||
"eslint-plugin-json": "^2.1.2",
|
"eslint-plugin-json": "^2.1.2",
|
||||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||||
"eslint-plugin-prettier": "^3.1.4",
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
"eslint-plugin-react": "^7.20.3",
|
"eslint-plugin-react": "^7.20.3",
|
||||||
"eslint-plugin-react-hooks": "^4.0.8",
|
"eslint-plugin-react-hooks": "^4.0.8",
|
||||||
"expect-playwright": "^0.3.4",
|
|
||||||
"express": "^4.17.1",
|
|
||||||
"glob": "^7.1.6",
|
"glob": "^7.1.6",
|
||||||
"jest": "^27.0.6",
|
"jest": "^28.1.1",
|
||||||
"jest-circus": "^27.0.6",
|
"node-sass": "^7.0.1",
|
||||||
"jest-dev-server": "^4.4.0",
|
"playwright": "^1.22.2",
|
||||||
"jest-environment-node": "^27.0.6",
|
"playwright-chromium": "^1.22.2",
|
||||||
"jest-playwright-preset": "^1.7.0",
|
"playwright-core": "^1.22.2",
|
||||||
"jest-runner": "^27.0.6",
|
"playwright-firefox": "^1.22.2",
|
||||||
"mkdirp": "^1.0.4",
|
"playwright-webkit": "^1.22.2",
|
||||||
"node-sass": "^4.14.1",
|
|
||||||
"playwright": "^1.12.3",
|
|
||||||
"playwright-chromium": "^1.12.3",
|
|
||||||
"playwright-core": "^1.12.3",
|
|
||||||
"playwright-firefox": "^1.12.3",
|
|
||||||
"playwright-webkit": "1.12.3",
|
|
||||||
"prettier": "^2.0.5",
|
"prettier": "^2.0.5",
|
||||||
"prettier-eslint": "^11.0.0",
|
"prettier-eslint": "^11.0.0",
|
||||||
"rollup": "^2.36.1",
|
"rollup": "^2.75.5",
|
||||||
|
"rollup-plugin-esbuild": "^4.9.1",
|
||||||
"rollup-plugin-livereload": "^2.0.0",
|
"rollup-plugin-livereload": "^2.0.0",
|
||||||
"rollup-plugin-prettier": "^2.1.0",
|
|
||||||
"rollup-plugin-serve": "^1.1.0",
|
"rollup-plugin-serve": "^1.1.0",
|
||||||
"rollup-plugin-styles": "^3.10.0",
|
"rollup-plugin-styles": "^3.10.0",
|
||||||
"rollup-plugin-terser": "^6.1.0",
|
"rollup-plugin-terser": "^6.1.0",
|
||||||
"rollup-plugin-typescript2": "^0.27.1",
|
"rollup-plugin-ts": "^3.0.1",
|
||||||
"should": "^13.2.3",
|
"should": "^13.2.3",
|
||||||
"tslib": "^2.2.0",
|
"tslib": "^2.4.0",
|
||||||
"typescript": "^4.3.2",
|
"typescript": "^4.7.3",
|
||||||
"utf-8-validate": "^5.0.2"
|
"utf-8-validate": "^5.0.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -72,6 +64,7 @@
|
|||||||
"test:browser": "yarn workspaces run test:browser",
|
"test:browser": "yarn workspaces run test:browser",
|
||||||
"test:browser:quick": "yarn workspaces run test:browser:quick",
|
"test:browser:quick": "yarn workspaces run test:browser:quick",
|
||||||
"test:browser-dev": "yarn workspaces run test:browser-dev",
|
"test:browser-dev": "yarn workspaces run test:browser-dev",
|
||||||
|
"test:playwright": "yarn workspaces run test:playwright",
|
||||||
"build": "yarn workspaces run build",
|
"build": "yarn workspaces run build",
|
||||||
"lint": "npx eslint --fix ."
|
"lint": "npx eslint --fix ."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
|
||||||
|
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#41D1FF"/>
|
||||||
|
<stop offset="1" stop-color="#BD34FE"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFEA83"/>
|
||||||
|
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
||||||
|
<stop offset="1" stop-color="#FFA800"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>OverlayScrollbars example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "overlayscrollbars-example",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^4.5.4",
|
||||||
|
"vite": "^2.9.9"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"overlayscrollbars": "file:./../overlayscrollbars"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import './style.css';
|
||||||
|
|
||||||
|
const app = document.querySelector<HTMLDivElement>('#app')!;
|
||||||
|
|
||||||
|
app.innerHTML = `
|
||||||
|
<h1>Hello Vite!</h1>
|
||||||
|
<a href="https://vitejs.dev/guide/features.html" target="_blank">Documentation</a>
|
||||||
|
`;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"strict": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
+817
-686
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+671
-748
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -3,12 +3,18 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"description": "OverlayScrollbars version 2",
|
"description": "OverlayScrollbars version 2",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
"files": [
|
||||||
|
"src",
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"types": "types/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest --coverage --runInBand --detectOpenHandles",
|
"test": "jest --coverage --runInBand --detectOpenHandles",
|
||||||
"test:jsdom": "jest --coverage --runInBand --detectOpenHandles --selectProjects jsdom --testPathPattern",
|
"test:jsdom": "jest --coverage --runInBand --detectOpenHandles --testPathPattern",
|
||||||
"test:browser": "jest --runInBand --detectOpenHandles --selectProjects browser --testPathPattern",
|
"test:browser": "jest --runInBand --detectOpenHandles --selectProjects browser --testPathPattern",
|
||||||
"test:browser:quick": "jest --runInBand --detectOpenHandles --selectProjects browser --testPathIgnorePatterns=\"/node_modules/|/structureLifecycle/\"",
|
"test:browser:quick": "jest --runInBand --detectOpenHandles --selectProjects browser --testPathIgnorePatterns=\"/node_modules/|/structureLifecycle/\"",
|
||||||
"test:browser-dev": "jest --runInBand --detectOpenHandles --selectProjects browser-dev --testPathPattern",
|
"test:browser-dev": "jest --runInBand --detectOpenHandles --selectProjects browser-dev --testPathPattern",
|
||||||
"build": "rollup -c"
|
"build": "rollup -c",
|
||||||
|
"test:playwright": "playwright test"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../../playwright.config.base');
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../../playwright.rollup.base');
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
const base = require('../../rollup.config.base');
|
const createRollupConfig = require('../../rollup.config.base');
|
||||||
|
const { devDependencies, peerDependencies } = require('./package.json');
|
||||||
|
|
||||||
const config = {
|
module.exports = createRollupConfig({
|
||||||
name: 'OverlayScrollbars',
|
project: 'OverlayScrollbars',
|
||||||
exports: 'auto',
|
rollup: {
|
||||||
globals: {
|
external: Object.keys(devDependencies || {}).concat(Object.keys(peerDependencies || {})),
|
||||||
jquery: 'jQuery',
|
output: {
|
||||||
|
exports: 'auto',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|
||||||
module.exports = (_, ...args) => base(config, ...args);
|
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
import { XY, WH, TRBL, CacheValues, PartialOptions, each, hasOwnProperty, isNumber, scrollLeft, scrollTop, assignDeep } from 'support';
|
import {
|
||||||
|
XY,
|
||||||
|
WH,
|
||||||
|
TRBL,
|
||||||
|
CacheValues,
|
||||||
|
PartialOptions,
|
||||||
|
each,
|
||||||
|
hasOwnProperty,
|
||||||
|
isNumber,
|
||||||
|
scrollLeft,
|
||||||
|
scrollTop,
|
||||||
|
assignDeep,
|
||||||
|
} from 'support';
|
||||||
import { OSOptions } from 'options';
|
import { OSOptions } from 'options';
|
||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
import { StructureSetup } from 'setups/structureSetup';
|
import { StructureSetup } from 'setups/structureSetup';
|
||||||
@@ -17,10 +29,7 @@ export type Lifecycle = (
|
|||||||
force: boolean
|
force: boolean
|
||||||
) => Partial<LifecycleAdaptiveUpdateHints> | void;
|
) => Partial<LifecycleAdaptiveUpdateHints> | void;
|
||||||
|
|
||||||
export interface LifecycleOptionInfo<T> {
|
export type LifecycleOptionInfo<T> = [T, boolean];
|
||||||
readonly _value: T;
|
|
||||||
_changed: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LifecycleCommunication {
|
export interface LifecycleCommunication {
|
||||||
_paddingInfo: {
|
_paddingInfo: {
|
||||||
@@ -64,13 +73,11 @@ export interface LifecycleHub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getPropByPath = <T>(obj: any, path: string): T =>
|
const getPropByPath = <T>(obj: any, path: string): T =>
|
||||||
obj ? path.split('.').reduce((o, prop) => (o && hasOwnProperty(o, prop) ? o[prop] : undefined), obj) : undefined;
|
obj
|
||||||
|
? path.split('.').reduce((o, prop) => (o && hasOwnProperty(o, prop) ? o[prop] : undefined), obj)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const booleanCacheValuesFallback: CacheValues<boolean> = {
|
const booleanCacheValuesFallback: CacheValues<boolean> = [false, false, false];
|
||||||
_value: false,
|
|
||||||
_previous: false,
|
|
||||||
_changed: false,
|
|
||||||
};
|
|
||||||
const lifecycleCommunicationFallback: LifecycleCommunication = {
|
const lifecycleCommunicationFallback: LifecycleCommunication = {
|
||||||
_paddingInfo: {
|
_paddingInfo: {
|
||||||
_absolute: false,
|
_absolute: false,
|
||||||
@@ -100,7 +107,11 @@ const lifecycleCommunicationFallback: LifecycleCommunication = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createLifecycleHub = (options: OSOptions, structureSetup: StructureSetup, scrollbarsSetup: ScrollbarsSetup): LifecycleHubInstance => {
|
export const createLifecycleHub = (
|
||||||
|
options: OSOptions,
|
||||||
|
structureSetup: StructureSetup,
|
||||||
|
scrollbarsSetup: ScrollbarsSetup
|
||||||
|
): LifecycleHubInstance => {
|
||||||
let lifecycleCommunication = lifecycleCommunicationFallback;
|
let lifecycleCommunication = lifecycleCommunicationFallback;
|
||||||
const { _viewport } = structureSetup._targetObj;
|
const { _viewport } = structureSetup._targetObj;
|
||||||
const {
|
const {
|
||||||
@@ -110,7 +121,8 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
|
|||||||
_addListener: addEnvironmentListener,
|
_addListener: addEnvironmentListener,
|
||||||
_removeListener: removeEnvironmentListener,
|
_removeListener: removeEnvironmentListener,
|
||||||
} = getEnvironment();
|
} = getEnvironment();
|
||||||
const doViewportArrange = !_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
|
const doViewportArrange =
|
||||||
|
!_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
|
||||||
const instance: LifecycleHub = {
|
const instance: LifecycleHub = {
|
||||||
_options: options,
|
_options: options,
|
||||||
_structureSetup: structureSetup,
|
_structureSetup: structureSetup,
|
||||||
@@ -120,11 +132,21 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
|
|||||||
lifecycleCommunication = assignDeep({}, lifecycleCommunication, newLifecycleCommunication);
|
lifecycleCommunication = assignDeep({}, lifecycleCommunication, newLifecycleCommunication);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const lifecycles: Lifecycle[] = [createTrinsicLifecycle(instance), createPaddingLifecycle(instance), createOverflowLifecycle(instance)];
|
const lifecycles: Lifecycle[] = [
|
||||||
|
createTrinsicLifecycle(instance),
|
||||||
|
createPaddingLifecycle(instance),
|
||||||
|
createOverflowLifecycle(instance),
|
||||||
|
];
|
||||||
|
|
||||||
const updateLifecycles = (updateHints?: Partial<LifecycleUpdateHints> | null, changedOptions?: Partial<OSOptions> | null, force?: boolean) => {
|
const updateLifecycles = (
|
||||||
|
updateHints?: Partial<LifecycleUpdateHints> | null,
|
||||||
|
changedOptions?: Partial<OSOptions> | null,
|
||||||
|
force?: boolean
|
||||||
|
) => {
|
||||||
let {
|
let {
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
_directionIsRTL,
|
_directionIsRTL,
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
_heightIntrinsic,
|
_heightIntrinsic,
|
||||||
_sizeChanged = force || false,
|
_sizeChanged = force || false,
|
||||||
_hostMutation = force || false,
|
_hostMutation = force || false,
|
||||||
@@ -133,13 +155,19 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
|
|||||||
} = updateHints || {};
|
} = updateHints || {};
|
||||||
|
|
||||||
const finalDirectionIsRTL =
|
const finalDirectionIsRTL =
|
||||||
_directionIsRTL || (_sizeObserver ? _sizeObserver._getCurrentCacheValues(force)._directionIsRTL : booleanCacheValuesFallback);
|
_directionIsRTL ||
|
||||||
|
(_sizeObserver
|
||||||
|
? _sizeObserver._getCurrentCacheValues(force)._directionIsRTL
|
||||||
|
: booleanCacheValuesFallback);
|
||||||
const finalHeightIntrinsic =
|
const finalHeightIntrinsic =
|
||||||
_heightIntrinsic || (_trinsicObserver ? _trinsicObserver._getCurrentCacheValues(force)._heightIntrinsic : booleanCacheValuesFallback);
|
_heightIntrinsic ||
|
||||||
const checkOption: LifecycleCheckOption = (path) => ({
|
(_trinsicObserver
|
||||||
_value: getPropByPath(options, path),
|
? _trinsicObserver._getCurrentCacheValues(force)._heightIntrinsic
|
||||||
_changed: force || getPropByPath(changedOptions, path) !== undefined,
|
: booleanCacheValuesFallback);
|
||||||
});
|
const checkOption: LifecycleCheckOption = (path) => [
|
||||||
|
getPropByPath(options, path),
|
||||||
|
force || getPropByPath(changedOptions, path) !== undefined,
|
||||||
|
];
|
||||||
const adjustScrollOffset = doViewportArrange || !_flexboxGlue;
|
const adjustScrollOffset = doViewportArrange || !_flexboxGlue;
|
||||||
const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
|
const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
|
||||||
const scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
|
const scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
|
||||||
@@ -186,9 +214,15 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
|
|||||||
options.callbacks.onUpdated();
|
options.callbacks.onUpdated();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const { _sizeObserver, _trinsicObserver, _updateObserverOptions, _destroy: destroyObservers } = lifecycleHubOservers(instance, updateLifecycles);
|
const {
|
||||||
|
_sizeObserver,
|
||||||
|
_trinsicObserver,
|
||||||
|
_updateObserverOptions,
|
||||||
|
_destroy: destroyObservers,
|
||||||
|
} = lifecycleHubOservers(instance, updateLifecycles);
|
||||||
|
|
||||||
const update = (changedOptions?: Partial<OSOptions> | null, force?: boolean) => updateLifecycles(null, changedOptions, force);
|
const update = (changedOptions?: Partial<OSOptions> | null, force?: boolean) =>
|
||||||
|
updateLifecycles(null, changedOptions, force);
|
||||||
const envUpdateListener = update.bind(null, null, true);
|
const envUpdateListener = update.bind(null, null, true);
|
||||||
addEnvironmentListener(envUpdateListener);
|
addEnvironmentListener(envUpdateListener);
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,40 @@
|
|||||||
import { CacheValues, diffClass, debounce, isArray, isNumber, each, indexOf, isString, attr, removeAttr } from 'support';
|
import {
|
||||||
|
CacheValues,
|
||||||
|
diffClass,
|
||||||
|
debounce,
|
||||||
|
isArray,
|
||||||
|
isNumber,
|
||||||
|
each,
|
||||||
|
indexOf,
|
||||||
|
isString,
|
||||||
|
attr,
|
||||||
|
removeAttr,
|
||||||
|
} from 'support';
|
||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
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';
|
||||||
import { LifecycleHub, LifecycleCheckOption, LifecycleUpdateHints } from 'lifecycles/lifecycleHub';
|
import { LifecycleHub, LifecycleCheckOption, LifecycleUpdateHints } from 'lifecycles/lifecycleHub';
|
||||||
|
|
||||||
//const hostSelector = `.${classNameHost}`;
|
// const hostSelector = `.${classNameHost}`;
|
||||||
|
|
||||||
// TODO: observer textarea attrs if textarea
|
// TODO: observer textarea attrs if textarea
|
||||||
// TODO: test _ignoreContentChange & _ignoreNestedTargetChange for content dom observer
|
// TODO: test _ignoreContentChange & _ignoreNestedTargetChange for content dom observer
|
||||||
// TODO: test _ignoreTargetChange for target dom observer
|
// TODO: test _ignoreTargetChange for target dom observer
|
||||||
|
|
||||||
//const viewportSelector = `.${classNameViewport}`;
|
// const viewportSelector = `.${classNameViewport}`;
|
||||||
//const contentSelector = `.${classNameContent}`;
|
// const contentSelector = `.${classNameContent}`;
|
||||||
const ignorePrefix = 'os-';
|
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) => {
|
const ignoreTargetChange = (
|
||||||
|
target: Node,
|
||||||
|
attrName: string,
|
||||||
|
oldValue: string | null,
|
||||||
|
newValue: string | null
|
||||||
|
) => {
|
||||||
if (attrName === 'class' && oldValue && newValue) {
|
if (attrName === 'class' && oldValue && newValue) {
|
||||||
const diff = diffClass(oldValue, newValue);
|
const diff = diffClass(oldValue, newValue);
|
||||||
return !!diff.find((addedOrRemovedClass) => addedOrRemovedClass.indexOf(ignorePrefix) !== 0);
|
return !!diff.find((addedOrRemovedClass) => addedOrRemovedClass.indexOf(ignorePrefix) !== 0);
|
||||||
@@ -26,7 +42,10 @@ const ignoreTargetChange = (target: Node, attrName: string, oldValue: string | n
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const lifecycleHubOservers = (instance: LifecycleHub, updateLifecycles: (updateHints?: Partial<LifecycleUpdateHints> | null) => unknown) => {
|
export const lifecycleHubOservers = (
|
||||||
|
instance: LifecycleHub,
|
||||||
|
updateLifecycles: (updateHints?: Partial<LifecycleUpdateHints> | null) => unknown
|
||||||
|
) => {
|
||||||
let debounceTimeout: number | false | undefined;
|
let debounceTimeout: number | false | undefined;
|
||||||
let debounceMaxDelay: number | false | undefined;
|
let debounceMaxDelay: number | false | undefined;
|
||||||
let contentMutationObserver: DOMObserver | undefined;
|
let contentMutationObserver: DOMObserver | undefined;
|
||||||
@@ -35,24 +54,37 @@ export const lifecycleHubOservers = (instance: LifecycleHub, updateLifecycles: (
|
|||||||
const { _host, _viewport, _content } = _targetObj;
|
const { _host, _viewport, _content } = _targetObj;
|
||||||
const { _isTextarea } = _targetCtx;
|
const { _isTextarea } = _targetCtx;
|
||||||
const { _nativeScrollbarStyling, _flexboxGlue } = getEnvironment();
|
const { _nativeScrollbarStyling, _flexboxGlue } = getEnvironment();
|
||||||
const contentMutationObserverAttr = _isTextarea ? baseStyleChangingAttrsTextarea : baseStyleChangingAttrs.concat(baseStyleChangingAttrsTextarea);
|
const contentMutationObserverAttr = _isTextarea
|
||||||
const updateLifecyclesWithDebouncedAdaptiveUpdateHints = debounce(updateLifecycles as (updateHints: Partial<LifecycleUpdateHints>) => any, {
|
? baseStyleChangingAttrsTextarea
|
||||||
_timeout: () => debounceTimeout,
|
: baseStyleChangingAttrs.concat(baseStyleChangingAttrsTextarea);
|
||||||
_maxDelay: () => debounceMaxDelay,
|
const updateLifecyclesWithDebouncedAdaptiveUpdateHints = debounce(
|
||||||
_mergeParams(prev, curr) {
|
updateLifecycles as (updateHints: Partial<LifecycleUpdateHints>) => any,
|
||||||
const { _sizeChanged: prevSizeChanged, _hostMutation: prevHostMutation, _contentMutation: prevContentMutation } = prev[0];
|
{
|
||||||
const { _sizeChanged: currSizeChanged, _hostMutation: currvHostMutation, _contentMutation: currContentMutation } = curr[0];
|
_timeout: () => debounceTimeout,
|
||||||
const merged: [Partial<LifecycleUpdateHints>] = [
|
_maxDelay: () => debounceMaxDelay,
|
||||||
{
|
_mergeParams(prev, curr) {
|
||||||
_sizeChanged: prevSizeChanged || currSizeChanged,
|
const {
|
||||||
_hostMutation: prevHostMutation || currvHostMutation,
|
_sizeChanged: prevSizeChanged,
|
||||||
_contentMutation: prevContentMutation || currContentMutation,
|
_hostMutation: prevHostMutation,
|
||||||
},
|
_contentMutation: prevContentMutation,
|
||||||
];
|
} = prev[0];
|
||||||
|
const {
|
||||||
|
_sizeChanged: currSizeChanged,
|
||||||
|
_hostMutation: currvHostMutation,
|
||||||
|
_contentMutation: currContentMutation,
|
||||||
|
} = curr[0];
|
||||||
|
const merged: [Partial<LifecycleUpdateHints>] = [
|
||||||
|
{
|
||||||
|
_sizeChanged: prevSizeChanged || currSizeChanged,
|
||||||
|
_hostMutation: prevHostMutation || currvHostMutation,
|
||||||
|
_contentMutation: prevContentMutation || currContentMutation,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return merged;
|
return merged;
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const updateViewportAttrsFromHost = (attributes?: string[]) => {
|
const updateViewportAttrsFromHost = (attributes?: string[]) => {
|
||||||
each(attributes || viewportAttrsFromTarget, (attribute) => {
|
each(attributes || viewportAttrsFromTarget, (attribute) => {
|
||||||
@@ -71,8 +103,15 @@ export const lifecycleHubOservers = (instance: LifecycleHub, updateLifecycles: (
|
|||||||
_heightIntrinsic: heightIntrinsic,
|
_heightIntrinsic: heightIntrinsic,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const onSizeChanged = ({ _sizeChanged, _directionIsRTLCache, _appear }: SizeObserverCallbackParams) => {
|
const onSizeChanged = ({
|
||||||
const updateFn = !_sizeChanged || _appear ? updateLifecycles : updateLifecyclesWithDebouncedAdaptiveUpdateHints;
|
_sizeChanged,
|
||||||
|
_directionIsRTLCache,
|
||||||
|
_appear,
|
||||||
|
}: SizeObserverCallbackParams) => {
|
||||||
|
const updateFn =
|
||||||
|
!_sizeChanged || _appear
|
||||||
|
? updateLifecycles
|
||||||
|
: updateLifecyclesWithDebouncedAdaptiveUpdateHints;
|
||||||
updateFn({
|
updateFn({
|
||||||
_sizeChanged,
|
_sizeChanged,
|
||||||
_directionIsRTL: _directionIsRTLCache,
|
_directionIsRTL: _directionIsRTLCache,
|
||||||
@@ -80,7 +119,9 @@ export const lifecycleHubOservers = (instance: LifecycleHub, updateLifecycles: (
|
|||||||
};
|
};
|
||||||
const onContentMutation = (contentChangedTroughEvent: boolean) => {
|
const onContentMutation = (contentChangedTroughEvent: boolean) => {
|
||||||
// if contentChangedTroughEvent is true its already debounced
|
// if contentChangedTroughEvent is true its already debounced
|
||||||
const updateFn = contentChangedTroughEvent ? updateLifecycles : updateLifecyclesWithDebouncedAdaptiveUpdateHints;
|
const updateFn = contentChangedTroughEvent
|
||||||
|
? updateLifecycles
|
||||||
|
: updateLifecyclesWithDebouncedAdaptiveUpdateHints;
|
||||||
updateFn({
|
updateFn({
|
||||||
_contentMutation: true,
|
_contentMutation: true,
|
||||||
});
|
});
|
||||||
@@ -95,8 +136,12 @@ export const lifecycleHubOservers = (instance: LifecycleHub, updateLifecycles: (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const trinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
const trinsicObserver =
|
||||||
const sizeObserver = createSizeObserver(_host, onSizeChanged, { _appear: true, _direction: !_nativeScrollbarStyling });
|
(_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
||||||
|
const sizeObserver = createSizeObserver(_host, onSizeChanged, {
|
||||||
|
_appear: true,
|
||||||
|
_direction: !_nativeScrollbarStyling,
|
||||||
|
});
|
||||||
const hostMutationObserver = createDOMObserver(_host, false, onHostMutation, {
|
const hostMutationObserver = createDOMObserver(_host, false, onHostMutation, {
|
||||||
_styleChangingAttributes: baseStyleChangingAttrs,
|
_styleChangingAttributes: baseStyleChangingAttrs,
|
||||||
_attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget),
|
_attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget),
|
||||||
@@ -104,9 +149,13 @@ export const lifecycleHubOservers = (instance: LifecycleHub, updateLifecycles: (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const updateOptions = (checkOption: LifecycleCheckOption) => {
|
const updateOptions = (checkOption: LifecycleCheckOption) => {
|
||||||
const { _value: elementEvents, _changed: elementEventsChanged } = checkOption<Array<[string, string]> | null>('updating.elementEvents');
|
const [elementEvents, elementEventsChanged] = checkOption<Array<[string, string]> | null>(
|
||||||
const { _value: attributes, _changed: attributesChanged } = checkOption<string[] | null>('updating.attributes');
|
'updating.elementEvents'
|
||||||
const { _value: debounce, _changed: debounceChanged } = checkOption<Array<number> | number | null>('updating.debounce');
|
);
|
||||||
|
const [attributes, attributesChanged] = checkOption<string[] | null>('updating.attributes');
|
||||||
|
const [debounceValue, debounceChanged] = checkOption<Array<number> | number | null>(
|
||||||
|
'updating.debounce'
|
||||||
|
);
|
||||||
const updateContentMutationObserver = elementEventsChanged || attributesChanged;
|
const updateContentMutationObserver = elementEventsChanged || attributesChanged;
|
||||||
|
|
||||||
if (updateContentMutationObserver) {
|
if (updateContentMutationObserver) {
|
||||||
@@ -119,7 +168,7 @@ export const lifecycleHubOservers = (instance: LifecycleHub, updateLifecycles: (
|
|||||||
_attributes: contentMutationObserverAttr.concat(attributes || []),
|
_attributes: contentMutationObserverAttr.concat(attributes || []),
|
||||||
_eventContentChange: elementEvents,
|
_eventContentChange: elementEvents,
|
||||||
_ignoreNestedTargetChange: ignoreTargetChange,
|
_ignoreNestedTargetChange: ignoreTargetChange,
|
||||||
//_nestedTargetSelector: hostSelector,
|
// _nestedTargetSelector: hostSelector,
|
||||||
/*
|
/*
|
||||||
_ignoreContentChange: (mutation, isNestedTarget) => {
|
_ignoreContentChange: (mutation, isNestedTarget) => {
|
||||||
const { target, attributeName } = mutation;
|
const { target, attributeName } = mutation;
|
||||||
@@ -135,13 +184,13 @@ export const lifecycleHubOservers = (instance: LifecycleHub, updateLifecycles: (
|
|||||||
|
|
||||||
if (debounceChanged) {
|
if (debounceChanged) {
|
||||||
updateLifecyclesWithDebouncedAdaptiveUpdateHints._flush();
|
updateLifecyclesWithDebouncedAdaptiveUpdateHints._flush();
|
||||||
if (isArray(debounce)) {
|
if (isArray(debounceValue)) {
|
||||||
const timeout = debounce[0];
|
const timeout = debounceValue[0];
|
||||||
const maxWait = debounce[1];
|
const maxWait = debounceValue[1];
|
||||||
debounceTimeout = isNumber(timeout) ? timeout : false;
|
debounceTimeout = isNumber(timeout) ? timeout : false;
|
||||||
debounceMaxDelay = isNumber(maxWait) ? maxWait : false;
|
debounceMaxDelay = isNumber(maxWait) ? maxWait : false;
|
||||||
} else if (isNumber(debounce)) {
|
} else if (isNumber(debounceValue)) {
|
||||||
debounceTimeout = debounce;
|
debounceTimeout = debounceValue;
|
||||||
debounceMaxDelay = false;
|
debounceMaxDelay = false;
|
||||||
} else {
|
} else {
|
||||||
debounceTimeout = false;
|
debounceTimeout = false;
|
||||||
|
|||||||
@@ -59,7 +59,12 @@ const sizeFraction = (elm: HTMLElement): WH<number> => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
const fractionalPixelRatioTollerance = () => (window.devicePixelRatio % 1 === 0 ? 0 : 1);
|
const fractionalPixelRatioTollerance = () => (window.devicePixelRatio % 1 === 0 ? 0 : 1);
|
||||||
const setAxisOverflowStyle = (horizontal: boolean, overflowAmount: number, behavior: OverflowBehavior, styleObj: StyleObject) => {
|
const setAxisOverflowStyle = (
|
||||||
|
horizontal: boolean,
|
||||||
|
overflowAmount: number,
|
||||||
|
behavior: OverflowBehavior,
|
||||||
|
styleObj: StyleObject
|
||||||
|
) => {
|
||||||
const overflowKey: keyof StyleObject = horizontal ? 'overflowX' : 'overflowY';
|
const overflowKey: keyof StyleObject = horizontal ? 'overflowX' : 'overflowY';
|
||||||
const behaviorIsVisible = behavior.indexOf('visible') === 0;
|
const behaviorIsVisible = behavior.indexOf('visible') === 0;
|
||||||
const behaviorIsVisibleHidden = behavior === 'visible-hidden';
|
const behaviorIsVisibleHidden = behavior === 'visible-hidden';
|
||||||
@@ -85,20 +90,43 @@ const setAxisOverflowStyle = (horizontal: boolean, overflowAmount: number, behav
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
|
export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
|
||||||
const { _structureSetup, _doViewportArrange, _getLifecycleCommunication, _setLifecycleCommunication } = lifecycleHub;
|
const {
|
||||||
|
_structureSetup,
|
||||||
|
_doViewportArrange,
|
||||||
|
_getLifecycleCommunication,
|
||||||
|
_setLifecycleCommunication,
|
||||||
|
} = lifecycleHub;
|
||||||
|
|
||||||
const { _host, _viewport, _viewportArrange } = _structureSetup._targetObj;
|
const { _host, _viewport, _viewportArrange } = _structureSetup._targetObj;
|
||||||
const { _update: updateViewportSizeFraction, _current: getCurrentViewportSizeFraction } = createCache<WH<number>>(
|
|
||||||
|
const [updateViewportSizeFraction, getCurrentViewportSizeFraction] = createCache<WH<number>>(
|
||||||
sizeFraction.bind(0, _viewport),
|
sizeFraction.bind(0, _viewport),
|
||||||
whCacheOptions
|
whCacheOptions
|
||||||
);
|
);
|
||||||
const { _update: updateViewportScrollSizeCache, _current: getCurrentViewportScrollSizeCache } = createCache<WH<number>>(
|
|
||||||
scrollSize.bind(0, _viewport),
|
const [updateViewportScrollSizeCache, getCurrentViewportScrollSizeCache] = createCache<
|
||||||
whCacheOptions
|
WH<number>
|
||||||
);
|
>(scrollSize.bind(0, _viewport), whCacheOptions);
|
||||||
const { _update: updateOverflowAmountCache, _current: getCurrentOverflowAmountCache } = createCache<WH<number>, OverflowAmountCacheContext>(
|
|
||||||
|
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] = createCache<
|
||||||
|
WH<number>,
|
||||||
|
OverflowAmountCacheContext
|
||||||
|
>(
|
||||||
({ _viewportScrollSize, _viewportClientSize, _viewportSizeFraction }) => ({
|
({ _viewportScrollSize, _viewportClientSize, _viewportSizeFraction }) => ({
|
||||||
w: max(0, round(max(0, _viewportScrollSize.w - _viewportClientSize.w) - (fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.w)))),
|
w: max(
|
||||||
h: max(0, round(max(0, _viewportScrollSize.h - _viewportClientSize.h) - (fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.h)))),
|
0,
|
||||||
|
round(
|
||||||
|
max(0, _viewportScrollSize.w - _viewportClientSize.w) -
|
||||||
|
(fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.w))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
h: max(
|
||||||
|
0,
|
||||||
|
round(
|
||||||
|
max(0, _viewportScrollSize.h - _viewportClientSize.h) -
|
||||||
|
(fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.h))
|
||||||
|
)
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
whCacheOptions
|
whCacheOptions
|
||||||
);
|
);
|
||||||
@@ -108,22 +136,35 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
* @param viewportOverflowState The current overflow state.
|
* @param viewportOverflowState The current overflow state.
|
||||||
* @param heightIntrinsic Whether the host height is intrinsic or not.
|
* @param heightIntrinsic Whether the host height is intrinsic or not.
|
||||||
*/
|
*/
|
||||||
const fixFlexboxGlue = (viewportOverflowState: ViewportOverflowState, heightIntrinsic: boolean) => {
|
const fixFlexboxGlue = (
|
||||||
|
viewportOverflowState: ViewportOverflowState,
|
||||||
|
heightIntrinsic: boolean
|
||||||
|
) => {
|
||||||
style(_viewport, {
|
style(_viewport, {
|
||||||
height: '',
|
height: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (heightIntrinsic) {
|
if (heightIntrinsic) {
|
||||||
const { _absolute: paddingAbsolute, _padding: padding } = _getLifecycleCommunication()._paddingInfo;
|
const { _nativeScrollbarIsOverlaid } = getEnvironment();
|
||||||
|
const {
|
||||||
|
_absolute: paddingAbsolute,
|
||||||
|
_padding: padding,
|
||||||
|
} = _getLifecycleCommunication()._paddingInfo;
|
||||||
const { _overflowScroll, _scrollbarsHideOffset } = viewportOverflowState;
|
const { _overflowScroll, _scrollbarsHideOffset } = viewportOverflowState;
|
||||||
const hostSizeFraction = sizeFraction(_host);
|
const hostSizeFraction = sizeFraction(_host);
|
||||||
const hostClientSize = clientSize(_host);
|
const hostClientSize = clientSize(_host);
|
||||||
// padding subtraction is only needed if padding is absolute or if viewport is content-box
|
// padding subtraction is only needed if padding is absolute or if viewport is content-box
|
||||||
const paddingVertical = paddingAbsolute || style(_viewport, 'boxSizing') === 'content-box' ? padding.b + padding.t : 0;
|
const isContentBox = style(_viewport, 'boxSizing') === 'content-box';
|
||||||
const fractionalClientHeight = hostClientSize.h + (abs(hostSizeFraction.h) < 1 ? hostSizeFraction.h : 0);
|
const paddingVertical = paddingAbsolute || isContentBox ? padding.b + padding.t : 0;
|
||||||
|
const fractionalClientHeight =
|
||||||
|
hostClientSize.h + (abs(hostSizeFraction.h) < 1 ? hostSizeFraction.h : 0);
|
||||||
|
const subtractXScrollbar = !(_nativeScrollbarIsOverlaid.x && isContentBox);
|
||||||
|
|
||||||
style(_viewport, {
|
style(_viewport, {
|
||||||
height: fractionalClientHeight + (_overflowScroll.x ? _scrollbarsHideOffset.x : 0) - paddingVertical,
|
height:
|
||||||
|
fractionalClientHeight +
|
||||||
|
(_overflowScroll.x && subtractXScrollbar ? _scrollbarsHideOffset.x : 0) -
|
||||||
|
paddingVertical,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -134,19 +175,39 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
* @param viewportStyleObj The viewport style object where the overflow scroll property can be read of, or undefined if shall be determined.
|
* @param viewportStyleObj The viewport style object where the overflow scroll property can be read of, or undefined if shall be determined.
|
||||||
* @returns A object which contains informations about the current overflow state.
|
* @returns A object which contains informations about the current overflow state.
|
||||||
*/
|
*/
|
||||||
const getViewportOverflowState = (showNativeOverlaidScrollbars: boolean, viewportStyleObj?: StyleObject): ViewportOverflowState => {
|
const getViewportOverflowState = (
|
||||||
const { _nativeScrollbarSize, _nativeScrollbarIsOverlaid, _nativeScrollbarStyling } = getEnvironment();
|
showNativeOverlaidScrollbars: boolean,
|
||||||
|
viewportStyleObj?: StyleObject
|
||||||
|
): ViewportOverflowState => {
|
||||||
|
const {
|
||||||
|
_nativeScrollbarSize,
|
||||||
|
_nativeScrollbarIsOverlaid,
|
||||||
|
_nativeScrollbarStyling,
|
||||||
|
} = getEnvironment();
|
||||||
const { x: overlaidX, y: overlaidY } = _nativeScrollbarIsOverlaid;
|
const { x: overlaidX, y: overlaidY } = _nativeScrollbarIsOverlaid;
|
||||||
const determineOverflow = !viewportStyleObj;
|
const determineOverflow = !viewportStyleObj;
|
||||||
const arrangeHideOffset = !_nativeScrollbarStyling && !showNativeOverlaidScrollbars ? overlaidScrollbarsHideOffset : 0;
|
const arrangeHideOffset =
|
||||||
const styleObj = determineOverflow ? style(_viewport, ['overflowX', 'overflowY']) : viewportStyleObj;
|
!_nativeScrollbarStyling && !showNativeOverlaidScrollbars ? overlaidScrollbarsHideOffset : 0;
|
||||||
|
const styleObj = determineOverflow
|
||||||
|
? style(_viewport, ['overflowX', 'overflowY'])
|
||||||
|
: viewportStyleObj;
|
||||||
const scroll = {
|
const scroll = {
|
||||||
x: styleObj!.overflowX === 'scroll',
|
x: styleObj!.overflowX === 'scroll',
|
||||||
y: styleObj!.overflowY === 'scroll',
|
y: styleObj!.overflowY === 'scroll',
|
||||||
};
|
};
|
||||||
const scrollbarsHideOffset = {
|
const scrollbarsHideOffset = {
|
||||||
x: scroll.x && !_nativeScrollbarStyling ? (overlaidX ? arrangeHideOffset : _nativeScrollbarSize.x) : 0,
|
x:
|
||||||
y: scroll.y && !_nativeScrollbarStyling ? (overlaidY ? arrangeHideOffset : _nativeScrollbarSize.y) : 0,
|
scroll.x && !_nativeScrollbarStyling
|
||||||
|
? overlaidX
|
||||||
|
? arrangeHideOffset
|
||||||
|
: _nativeScrollbarSize.x
|
||||||
|
: 0,
|
||||||
|
y:
|
||||||
|
scroll.y && !_nativeScrollbarStyling
|
||||||
|
? overlaidY
|
||||||
|
? arrangeHideOffset
|
||||||
|
: _nativeScrollbarSize.y
|
||||||
|
: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -173,8 +234,18 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
overflow: OverflowOption,
|
overflow: OverflowOption,
|
||||||
viewportStyleObj: StyleObject
|
viewportStyleObj: StyleObject
|
||||||
): ViewportOverflowState => {
|
): ViewportOverflowState => {
|
||||||
const { _visible: xVisible, _behavior: xVisibleBehavior } = setAxisOverflowStyle(true, overflowAmount!.w, overflow.x, viewportStyleObj);
|
const { _visible: xVisible, _behavior: xVisibleBehavior } = setAxisOverflowStyle(
|
||||||
const { _visible: yVisible, _behavior: yVisibleBehavior } = setAxisOverflowStyle(false, overflowAmount!.h, overflow.y, viewportStyleObj);
|
true,
|
||||||
|
overflowAmount!.w,
|
||||||
|
overflow.x,
|
||||||
|
viewportStyleObj
|
||||||
|
);
|
||||||
|
const { _visible: yVisible, _behavior: yVisibleBehavior } = setAxisOverflowStyle(
|
||||||
|
false,
|
||||||
|
overflowAmount!.h,
|
||||||
|
overflow.y,
|
||||||
|
viewportStyleObj
|
||||||
|
);
|
||||||
|
|
||||||
if (xVisible && !yVisible) {
|
if (xVisible && !yVisible) {
|
||||||
viewportStyleObj.overflowX = xVisibleBehavior;
|
viewportStyleObj.overflowX = xVisibleBehavior;
|
||||||
@@ -204,14 +275,26 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
const { x: arrangeX, y: arrangeY } = _scrollbarsHideOffsetArrange;
|
const { x: arrangeX, y: arrangeY } = _scrollbarsHideOffsetArrange;
|
||||||
const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset;
|
const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset;
|
||||||
const { _viewportPaddingStyle: viewportPaddingStyle } = _getLifecycleCommunication();
|
const { _viewportPaddingStyle: viewportPaddingStyle } = _getLifecycleCommunication();
|
||||||
const viewportArrangeHorizontalPaddingKey: keyof StyleObject = directionIsRTL ? 'paddingRight' : 'paddingLeft';
|
const viewportArrangeHorizontalPaddingKey: keyof StyleObject = directionIsRTL
|
||||||
const viewportArrangeHorizontalPaddingValue = viewportPaddingStyle[viewportArrangeHorizontalPaddingKey] as number;
|
? 'paddingRight'
|
||||||
|
: 'paddingLeft';
|
||||||
|
const viewportArrangeHorizontalPaddingValue = viewportPaddingStyle[
|
||||||
|
viewportArrangeHorizontalPaddingKey
|
||||||
|
] as number;
|
||||||
const viewportArrangeVerticalPaddingValue = viewportPaddingStyle.paddingTop as number;
|
const viewportArrangeVerticalPaddingValue = viewportPaddingStyle.paddingTop as number;
|
||||||
const fractionalContentWidth = viewportScrollSize.w + (abs(viewportSizeFraction.w) < 1 ? viewportSizeFraction.w : 0);
|
const fractionalContentWidth =
|
||||||
const fractionalContenHeight = viewportScrollSize.h + (abs(viewportSizeFraction.h) < 1 ? viewportSizeFraction.h : 0);
|
viewportScrollSize.w + (abs(viewportSizeFraction.w) < 1 ? viewportSizeFraction.w : 0);
|
||||||
|
const fractionalContenHeight =
|
||||||
|
viewportScrollSize.h + (abs(viewportSizeFraction.h) < 1 ? viewportSizeFraction.h : 0);
|
||||||
const arrangeSize = {
|
const arrangeSize = {
|
||||||
w: hideOffsetY && arrangeY ? `${hideOffsetY + fractionalContentWidth - viewportArrangeHorizontalPaddingValue}px` : '',
|
w:
|
||||||
h: hideOffsetX && arrangeX ? `${hideOffsetX + fractionalContenHeight - viewportArrangeVerticalPaddingValue}px` : '',
|
hideOffsetY && arrangeY
|
||||||
|
? `${hideOffsetY + fractionalContentWidth - viewportArrangeHorizontalPaddingValue}px`
|
||||||
|
: '',
|
||||||
|
h:
|
||||||
|
hideOffsetX && arrangeX
|
||||||
|
? `${hideOffsetX + fractionalContenHeight - viewportArrangeVerticalPaddingValue}px`
|
||||||
|
: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
// adjust content arrange / before element
|
// adjust content arrange / before element
|
||||||
@@ -221,7 +304,10 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
const { cssRules } = sheet;
|
const { cssRules } = sheet;
|
||||||
if (cssRules) {
|
if (cssRules) {
|
||||||
if (!cssRules.length) {
|
if (!cssRules.length) {
|
||||||
sheet.insertRule(`#${attr(_viewportArrange, 'id')} + .${classNameViewportArrange}::before {}`, 0);
|
sheet.insertRule(
|
||||||
|
`#${attr(_viewportArrange, 'id')} + .${classNameViewportArrange}::before {}`,
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -260,7 +346,9 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset;
|
const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset;
|
||||||
const { _viewportPaddingStyle: viewportPaddingStyle } = _getLifecycleCommunication();
|
const { _viewportPaddingStyle: viewportPaddingStyle } = _getLifecycleCommunication();
|
||||||
const horizontalMarginKey: keyof StyleObject = directionIsRTL ? 'marginLeft' : 'marginRight';
|
const horizontalMarginKey: keyof StyleObject = directionIsRTL ? 'marginLeft' : 'marginRight';
|
||||||
const viewportHorizontalPaddingKey: keyof StyleObject = directionIsRTL ? 'paddingLeft' : 'paddingRight';
|
const viewportHorizontalPaddingKey: keyof StyleObject = directionIsRTL
|
||||||
|
? 'paddingLeft'
|
||||||
|
: 'paddingRight';
|
||||||
const horizontalMarginValue = viewportPaddingStyle[horizontalMarginKey] as number;
|
const horizontalMarginValue = viewportPaddingStyle[horizontalMarginKey] as number;
|
||||||
const verticalMarginValue = viewportPaddingStyle.marginBottom as number;
|
const verticalMarginValue = viewportPaddingStyle.marginBottom as number;
|
||||||
const horizontalPaddingValue = viewportPaddingStyle[viewportHorizontalPaddingKey] as number;
|
const horizontalPaddingValue = viewportPaddingStyle[viewportHorizontalPaddingKey] as number;
|
||||||
@@ -275,7 +363,8 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
|
|
||||||
// viewport arrange additional styles
|
// viewport arrange additional styles
|
||||||
if (viewportArrange) {
|
if (viewportArrange) {
|
||||||
viewportStyleObj[viewportHorizontalPaddingKey] = horizontalPaddingValue + (arrangeY ? hideOffsetY : 0);
|
viewportStyleObj[viewportHorizontalPaddingKey] =
|
||||||
|
horizontalPaddingValue + (arrangeY ? hideOffsetY : 0);
|
||||||
viewportStyleObj.paddingBottom = verticalPaddingValue + (arrangeX ? hideOffsetX : 0);
|
viewportStyleObj.paddingBottom = verticalPaddingValue + (arrangeX ? hideOffsetX : 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -293,7 +382,8 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
viewportOverflowState?: ViewportOverflowState
|
viewportOverflowState?: ViewportOverflowState
|
||||||
): UndoViewportArrangeResult => {
|
): UndoViewportArrangeResult => {
|
||||||
if (_doViewportArrange) {
|
if (_doViewportArrange) {
|
||||||
const finalViewportOverflowState = viewportOverflowState || getViewportOverflowState(showNativeOverlaidScrollbars);
|
const finalViewportOverflowState =
|
||||||
|
viewportOverflowState || getViewportOverflowState(showNativeOverlaidScrollbars);
|
||||||
const { _viewportPaddingStyle: viewportPaddingStyle } = _getLifecycleCommunication();
|
const { _viewportPaddingStyle: viewportPaddingStyle } = _getLifecycleCommunication();
|
||||||
const { _flexboxGlue } = getEnvironment();
|
const { _flexboxGlue } = getEnvironment();
|
||||||
const { _scrollbarsHideOffsetArrange } = finalViewportOverflowState;
|
const { _scrollbarsHideOffsetArrange } = finalViewportOverflowState;
|
||||||
@@ -322,7 +412,12 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
_redoViewportArrange: () => {
|
_redoViewportArrange: () => {
|
||||||
hideNativeScrollbars(finalViewportOverflowState, directionIsRTL, _doViewportArrange, prevStyle);
|
hideNativeScrollbars(
|
||||||
|
finalViewportOverflowState,
|
||||||
|
directionIsRTL,
|
||||||
|
_doViewportArrange,
|
||||||
|
prevStyle
|
||||||
|
);
|
||||||
style(_viewport, prevStyle);
|
style(_viewport, prevStyle);
|
||||||
addClass(_viewport, classNameViewportArrange);
|
addClass(_viewport, classNameViewportArrange);
|
||||||
},
|
},
|
||||||
@@ -335,16 +430,32 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (updateHints, checkOption, force) => {
|
return (updateHints, checkOption, force) => {
|
||||||
const { _directionIsRTL, _heightIntrinsic, _sizeChanged, _hostMutation, _contentMutation, _paddingStyleChanged } = updateHints;
|
const {
|
||||||
|
_directionIsRTL,
|
||||||
|
_heightIntrinsic,
|
||||||
|
_sizeChanged,
|
||||||
|
_hostMutation,
|
||||||
|
_contentMutation,
|
||||||
|
_paddingStyleChanged,
|
||||||
|
} = updateHints;
|
||||||
const { _flexboxGlue, _nativeScrollbarStyling, _nativeScrollbarIsOverlaid } = getEnvironment();
|
const { _flexboxGlue, _nativeScrollbarStyling, _nativeScrollbarIsOverlaid } = getEnvironment();
|
||||||
const { _value: heightIntrinsic, _changed: heightIntrinsicChanged } = _heightIntrinsic;
|
const [heightIntrinsic, heightIntrinsicChanged] = _heightIntrinsic;
|
||||||
const { _value: directionIsRTL, _changed: directionChanged } = _directionIsRTL;
|
const [directionIsRTL, directionChanged] = _directionIsRTL;
|
||||||
const { _value: showNativeOverlaidScrollbarsOption, _changed: showNativeOverlaidScrollbarsChanged } = checkOption<boolean>(
|
const [showNativeOverlaidScrollbarsOption, showNativeOverlaidScrollbarsChanged] = checkOption<
|
||||||
'nativeScrollbarsOverlaid.show'
|
boolean
|
||||||
);
|
>('nativeScrollbarsOverlaid.show');
|
||||||
const showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y;
|
const showNativeOverlaidScrollbars =
|
||||||
|
showNativeOverlaidScrollbarsOption &&
|
||||||
|
_nativeScrollbarIsOverlaid.x &&
|
||||||
|
_nativeScrollbarIsOverlaid.y;
|
||||||
const adjustFlexboxGlue =
|
const adjustFlexboxGlue =
|
||||||
!_flexboxGlue && (_sizeChanged || _contentMutation || _hostMutation || showNativeOverlaidScrollbarsChanged || heightIntrinsicChanged);
|
!_flexboxGlue &&
|
||||||
|
(_sizeChanged ||
|
||||||
|
_contentMutation ||
|
||||||
|
_hostMutation ||
|
||||||
|
showNativeOverlaidScrollbarsChanged ||
|
||||||
|
heightIntrinsicChanged);
|
||||||
|
|
||||||
let viewportSizeFractionCache: CacheValues<WH<number>> = getCurrentViewportSizeFraction(force);
|
let viewportSizeFractionCache: CacheValues<WH<number>> = getCurrentViewportSizeFraction(force);
|
||||||
let viewportScrollSizeCache: CacheValues<WH<number>> = getCurrentViewportScrollSizeCache(force);
|
let viewportScrollSizeCache: CacheValues<WH<number>> = getCurrentViewportScrollSizeCache(force);
|
||||||
let overflowAmuntCache: CacheValues<WH<number>> = getCurrentOverflowAmountCache(force);
|
let overflowAmuntCache: CacheValues<WH<number>> = getCurrentOverflowAmountCache(force);
|
||||||
@@ -363,14 +474,29 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
fixFlexboxGlue(preMeasureViewportOverflowState, !!heightIntrinsic);
|
fixFlexboxGlue(preMeasureViewportOverflowState, !!heightIntrinsic);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_sizeChanged || _paddingStyleChanged || _contentMutation || showNativeOverlaidScrollbarsChanged || directionChanged) {
|
if (
|
||||||
const { _redoViewportArrange, _viewportOverflowState: undoViewportArrangeOverflowState } = undoViewportArrange(
|
_sizeChanged ||
|
||||||
|
_paddingStyleChanged ||
|
||||||
|
_contentMutation ||
|
||||||
|
showNativeOverlaidScrollbarsChanged ||
|
||||||
|
directionChanged
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
_redoViewportArrange,
|
||||||
|
_viewportOverflowState: undoViewportArrangeOverflowState,
|
||||||
|
} = undoViewportArrange(
|
||||||
showNativeOverlaidScrollbars,
|
showNativeOverlaidScrollbars,
|
||||||
directionIsRTL!,
|
directionIsRTL!,
|
||||||
preMeasureViewportOverflowState
|
preMeasureViewportOverflowState
|
||||||
);
|
);
|
||||||
const { _value: viewportSizeFraction, _changed: viewportSizeFractionCahnged } = (viewportSizeFractionCache = updateViewportSizeFraction(force));
|
const [
|
||||||
const { _value: viewportScrollSize, _changed: viewportScrollSizeChanged } = (viewportScrollSizeCache = updateViewportScrollSizeCache(force));
|
viewportSizeFraction,
|
||||||
|
viewportSizeFractionCahnged,
|
||||||
|
] = (viewportSizeFractionCache = updateViewportSizeFraction(force));
|
||||||
|
const [
|
||||||
|
viewportScrollSize,
|
||||||
|
viewportScrollSizeChanged,
|
||||||
|
] = (viewportScrollSizeCache = updateViewportScrollSizeCache(force));
|
||||||
const viewportContentSize = clientSize(_viewport);
|
const viewportContentSize = clientSize(_viewport);
|
||||||
let arrangedViewportScrollSize = viewportScrollSize!;
|
let arrangedViewportScrollSize = viewportScrollSize!;
|
||||||
let arrangedViewportClientSize = viewportContentSize;
|
let arrangedViewportClientSize = viewportContentSize;
|
||||||
@@ -379,10 +505,17 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
|
|
||||||
// if re measure is required (only required if content arrange strategy is used)
|
// if re measure is required (only required if content arrange strategy is used)
|
||||||
if (
|
if (
|
||||||
(viewportScrollSizeChanged || viewportSizeFractionCahnged || showNativeOverlaidScrollbarsChanged) &&
|
(viewportScrollSizeChanged ||
|
||||||
|
viewportSizeFractionCahnged ||
|
||||||
|
showNativeOverlaidScrollbarsChanged) &&
|
||||||
undoViewportArrangeOverflowState &&
|
undoViewportArrangeOverflowState &&
|
||||||
!showNativeOverlaidScrollbars &&
|
!showNativeOverlaidScrollbars &&
|
||||||
arrangeViewport(undoViewportArrangeOverflowState, viewportScrollSize!, viewportSizeFraction!, directionIsRTL!)
|
arrangeViewport(
|
||||||
|
undoViewportArrangeOverflowState,
|
||||||
|
viewportScrollSize!,
|
||||||
|
viewportSizeFraction!,
|
||||||
|
directionIsRTL!
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
arrangedViewportClientSize = clientSize(_viewport);
|
arrangedViewportClientSize = clientSize(_viewport);
|
||||||
arrangedViewportScrollSize = scrollSize(_viewport);
|
arrangedViewportScrollSize = scrollSize(_viewport);
|
||||||
@@ -401,10 +534,10 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { _value: viewportSizeFraction, _changed: viewportSizeFractionChanged } = viewportSizeFractionCache;
|
const [viewportSizeFraction, viewportSizeFractionChanged] = viewportSizeFractionCache;
|
||||||
const { _value: viewportScrollSize, _changed: viewportScrollSizeChanged } = viewportScrollSizeCache;
|
const [viewportScrollSize, viewportScrollSizeChanged] = viewportScrollSizeCache;
|
||||||
const { _value: overflowAmount, _changed: overflowAmountChanged } = overflowAmuntCache;
|
const [overflowAmount, overflowAmountChanged] = overflowAmuntCache;
|
||||||
const { _value: overflow, _changed: overflowChanged } = checkOption<OverflowOption>('overflow');
|
const [overflow, overflowChanged] = checkOption<OverflowOption>('overflow');
|
||||||
|
|
||||||
if (
|
if (
|
||||||
_paddingStyleChanged ||
|
_paddingStyleChanged ||
|
||||||
@@ -425,8 +558,18 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
overflowX: '',
|
overflowX: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewportOverflowState = setViewportOverflowState(showNativeOverlaidScrollbars, overflowAmount!, overflow, viewportStyle);
|
const viewportOverflowState = setViewportOverflowState(
|
||||||
const viewportArranged = arrangeViewport(viewportOverflowState, viewportScrollSize!, viewportSizeFraction!, directionIsRTL!);
|
showNativeOverlaidScrollbars,
|
||||||
|
overflowAmount!,
|
||||||
|
overflow,
|
||||||
|
viewportStyle
|
||||||
|
);
|
||||||
|
const viewportArranged = arrangeViewport(
|
||||||
|
viewportOverflowState,
|
||||||
|
viewportScrollSize!,
|
||||||
|
viewportSizeFraction!,
|
||||||
|
directionIsRTL!
|
||||||
|
);
|
||||||
hideNativeScrollbars(viewportOverflowState, directionIsRTL!, viewportArranged, viewportStyle);
|
hideNativeScrollbars(viewportOverflowState, directionIsRTL!, viewportArranged, viewportStyle);
|
||||||
|
|
||||||
if (adjustFlexboxGlue) {
|
if (adjustFlexboxGlue) {
|
||||||
|
|||||||
@@ -11,21 +11,24 @@ import { getEnvironment } from 'environment';
|
|||||||
export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
|
export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
|
||||||
const { _structureSetup, _setLifecycleCommunication } = lifecycleHub;
|
const { _structureSetup, _setLifecycleCommunication } = lifecycleHub;
|
||||||
const { _host, _padding, _viewport } = _structureSetup._targetObj;
|
const { _host, _padding, _viewport } = _structureSetup._targetObj;
|
||||||
const { _update: updatePaddingCache, _current: currentPaddingCache } = createCache<TRBL>(topRightBottomLeft.bind(0, _host, 'padding'), {
|
const [updatePaddingCache, currentPaddingCache] = createCache<TRBL>(
|
||||||
_equal: equalTRBL,
|
topRightBottomLeft.bind(0, _host, 'padding'),
|
||||||
_initialValue: topRightBottomLeft(),
|
{
|
||||||
});
|
_equal: equalTRBL,
|
||||||
|
_initialValue: topRightBottomLeft(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (updateHints, checkOption, force) => {
|
return (updateHints, checkOption, force) => {
|
||||||
let { _value: padding, _changed: paddingChanged } = currentPaddingCache(force);
|
let [padding, paddingChanged] = currentPaddingCache(force);
|
||||||
const { _nativeScrollbarStyling, _flexboxGlue } = getEnvironment();
|
const { _nativeScrollbarStyling, _flexboxGlue } = getEnvironment();
|
||||||
const { _sizeChanged, _directionIsRTL, _contentMutation } = updateHints;
|
const { _sizeChanged, _directionIsRTL, _contentMutation } = updateHints;
|
||||||
const { _value: directionIsRTL, _changed: directionChanged } = _directionIsRTL;
|
const [directionIsRTL, directionChanged] = _directionIsRTL;
|
||||||
const { _value: paddingAbsolute, _changed: paddingAbsoluteChanged } = checkOption('paddingAbsolute');
|
const [paddingAbsolute, paddingAbsoluteChanged] = checkOption('paddingAbsolute');
|
||||||
const contentMutation = !_flexboxGlue && _contentMutation;
|
const contentMutation = !_flexboxGlue && _contentMutation;
|
||||||
|
|
||||||
if (_sizeChanged || paddingChanged || contentMutation) {
|
if (_sizeChanged || paddingChanged || contentMutation) {
|
||||||
({ _value: padding, _changed: paddingChanged } = updatePaddingCache(force));
|
[padding, paddingChanged] = updatePaddingCache(force);
|
||||||
}
|
}
|
||||||
|
|
||||||
const paddingStyleChanged = paddingAbsoluteChanged || directionChanged || paddingChanged;
|
const paddingStyleChanged = paddingAbsoluteChanged || directionChanged || paddingChanged;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const createTrinsicLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =>
|
|||||||
|
|
||||||
return (updateHints) => {
|
return (updateHints) => {
|
||||||
const { _heightIntrinsic } = updateHints;
|
const { _heightIntrinsic } = updateHints;
|
||||||
const { _value: heightIntrinsic, _changed: heightIntrinsicChanged } = _heightIntrinsic;
|
const [heightIntrinsic, heightIntrinsicChanged] = _heightIntrinsic;
|
||||||
|
|
||||||
if (heightIntrinsicChanged) {
|
if (heightIntrinsicChanged) {
|
||||||
style(_content, {
|
style(_content, {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
isArray,
|
isArray,
|
||||||
isBoolean,
|
isBoolean,
|
||||||
removeClass,
|
removeClass,
|
||||||
|
isObject,
|
||||||
} from 'support';
|
} from 'support';
|
||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
import {
|
import {
|
||||||
@@ -53,24 +54,10 @@ export interface SizeObserver {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
const directionIsRTLMap = {
|
|
||||||
direction: ['rtl'],
|
|
||||||
'writing-mode': ['sideways-rl', 'tb', 'tb-rl', 'vertical-rl'],
|
|
||||||
};
|
|
||||||
const directionIsRTL = (elm: HTMLElement): boolean => {
|
|
||||||
let isRTL = false;
|
|
||||||
const styles = style(elm, ['direction', 'writing-mode']);
|
|
||||||
each(styles, (value, key) => {
|
|
||||||
isRTL = isRTL || indexOf(directionIsRTLMap[key], value) > -1;
|
|
||||||
});
|
|
||||||
return isRTL;
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
const animationStartEventName = 'animationstart';
|
const animationStartEventName = 'animationstart';
|
||||||
const scrollEventName = 'scroll';
|
const scrollEventName = 'scroll';
|
||||||
const scrollAmount = 3333333;
|
const scrollAmount = 3333333;
|
||||||
const directionIsRTL = (elm: HTMLElement): boolean => style(elm, 'direction') === 'rtl';
|
const getElmDirectionIsRTL = (elm: HTMLElement): boolean => style(elm, 'direction') === 'rtl';
|
||||||
const domRectHasDimensions = (rect?: DOMRectReadOnly) => rect && (rect.height || rect.width);
|
const domRectHasDimensions = (rect?: DOMRectReadOnly) => rect && (rect.height || rect.width);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,12 +72,20 @@ export const createSizeObserver = (
|
|||||||
onSizeChangedCallback: (params: SizeObserverCallbackParams) => any,
|
onSizeChangedCallback: (params: SizeObserverCallbackParams) => any,
|
||||||
options?: SizeObserverOptions
|
options?: SizeObserverOptions
|
||||||
): SizeObserver => {
|
): SizeObserver => {
|
||||||
const { _direction: observeDirectionChange = false, _appear: observeAppearChange = false } = options || {};
|
const { _direction: observeDirectionChange = false, _appear: observeAppearChange = false } =
|
||||||
|
options || {};
|
||||||
const { _rtlScrollBehavior: rtlScrollBehavior } = getEnvironment();
|
const { _rtlScrollBehavior: rtlScrollBehavior } = getEnvironment();
|
||||||
const baseElements = createDOM(`<div class="${classNameSizeObserver}"><div class="${classNameSizeObserverListener}"></div></div>`);
|
const baseElements = createDOM(
|
||||||
|
`<div class="${classNameSizeObserver}"><div class="${classNameSizeObserverListener}"></div></div>`
|
||||||
|
);
|
||||||
const sizeObserver = baseElements[0] as HTMLElement;
|
const sizeObserver = baseElements[0] as HTMLElement;
|
||||||
const listenerElement = sizeObserver.firstChild as HTMLElement;
|
const listenerElement = sizeObserver.firstChild as HTMLElement;
|
||||||
const { _update: updateResizeObserverContentRectCache } = createCache<DOMRectReadOnly, DOMRectReadOnly>(0, {
|
const getIsDirectionRTL = getElmDirectionIsRTL.bind(0, sizeObserver);
|
||||||
|
const [updateResizeObserverContentRectCache] = createCache<
|
||||||
|
DOMRectReadOnly | undefined,
|
||||||
|
DOMRectReadOnly
|
||||||
|
>(0, {
|
||||||
|
_initialValue: undefined,
|
||||||
_alwaysUpdateValues: true,
|
_alwaysUpdateValues: true,
|
||||||
_equal: (currVal, newVal) =>
|
_equal: (currVal, newVal) =>
|
||||||
!(
|
!(
|
||||||
@@ -99,26 +94,37 @@ export const createSizeObserver = (
|
|||||||
(!domRectHasDimensions(currVal) && domRectHasDimensions(newVal))
|
(!domRectHasDimensions(currVal) && domRectHasDimensions(newVal))
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
const onSizeChangedCallbackProxy = (sizeChangedContext?: CacheValues<boolean> | ResizeObserverEntry[] | Event | boolean) => {
|
const onSizeChangedCallbackProxy = (
|
||||||
const hasDirectionCache = sizeChangedContext && isBoolean((sizeChangedContext as CacheValues<boolean>)._value);
|
sizeChangedContext?: CacheValues<boolean> | ResizeObserverEntry[] | Event | boolean
|
||||||
|
) => {
|
||||||
|
const isResizeObserverCall =
|
||||||
|
isArray(sizeChangedContext) &&
|
||||||
|
sizeChangedContext.length > 0 &&
|
||||||
|
isObject(sizeChangedContext[0]);
|
||||||
|
|
||||||
|
const hasDirectionCache =
|
||||||
|
!isResizeObserverCall && isBoolean((sizeChangedContext as CacheValues<boolean>)[0]);
|
||||||
|
|
||||||
let skip = false;
|
let skip = false;
|
||||||
let appear: boolean | number | undefined = false;
|
let appear: boolean | number | undefined = false;
|
||||||
let doDirectionScroll = true; // always true if sizeChangedContext is Event (appear callback or RO. Polyfill)
|
let doDirectionScroll = true; // always true if sizeChangedContext is Event (appear callback or RO. Polyfill)
|
||||||
|
|
||||||
// if triggered from RO.
|
// if triggered from RO.
|
||||||
if (isArray(sizeChangedContext) && sizeChangedContext.length > 0) {
|
if (isResizeObserverCall) {
|
||||||
const { _previous, _value } = updateResizeObserverContentRectCache(0, sizeChangedContext.pop()!.contentRect);
|
const [currRContentRect, , prevContentRect] = updateResizeObserverContentRectCache(
|
||||||
const hasDimensions = domRectHasDimensions(_value);
|
0,
|
||||||
const hadDimensions = domRectHasDimensions(_previous);
|
(sizeChangedContext as ResizeObserverEntry[]).pop()!.contentRect
|
||||||
skip = !_previous || !hasDimensions; // skip on initial RO. call or if display is none
|
);
|
||||||
|
const hasDimensions = domRectHasDimensions(currRContentRect);
|
||||||
|
const hadDimensions = domRectHasDimensions(prevContentRect);
|
||||||
|
skip = !prevContentRect || !hasDimensions; // skip on initial RO. call or if display is none
|
||||||
appear = !hadDimensions && hasDimensions;
|
appear = !hadDimensions && hasDimensions;
|
||||||
|
|
||||||
doDirectionScroll = !skip; // direction scroll when not skipping
|
doDirectionScroll = !skip; // direction scroll when not skipping
|
||||||
}
|
}
|
||||||
// else if its triggered with DirectionCache
|
// else if its triggered with DirectionCache
|
||||||
else if (hasDirectionCache) {
|
else if (hasDirectionCache) {
|
||||||
doDirectionScroll = (sizeChangedContext as CacheValues<boolean>)._changed; // direction scroll when DirectionCache changed, false otherwise
|
[, doDirectionScroll] = sizeChangedContext as CacheValues<boolean>; // direction scroll when DirectionCache changed, false otherwise
|
||||||
}
|
}
|
||||||
// else if it triggered with appear from polyfill
|
// else if it triggered with appear from polyfill
|
||||||
else {
|
else {
|
||||||
@@ -126,21 +132,36 @@ export const createSizeObserver = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (observeDirectionChange && doDirectionScroll) {
|
if (observeDirectionChange && doDirectionScroll) {
|
||||||
const rtl = hasDirectionCache ? (sizeChangedContext as CacheValues<boolean>)._value : directionIsRTL(sizeObserver);
|
const rtl = hasDirectionCache
|
||||||
scrollLeft(sizeObserver, rtl ? (rtlScrollBehavior.n ? -scrollAmount : rtlScrollBehavior.i ? 0 : scrollAmount) : scrollAmount);
|
? (sizeChangedContext as CacheValues<boolean>)[0]
|
||||||
|
: getElmDirectionIsRTL(sizeObserver);
|
||||||
|
scrollLeft(
|
||||||
|
sizeObserver,
|
||||||
|
rtl
|
||||||
|
? rtlScrollBehavior.n
|
||||||
|
? -scrollAmount
|
||||||
|
: rtlScrollBehavior.i
|
||||||
|
? 0
|
||||||
|
: scrollAmount
|
||||||
|
: scrollAmount
|
||||||
|
);
|
||||||
scrollTop(sizeObserver, scrollAmount);
|
scrollTop(sizeObserver, scrollAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skip) {
|
if (!skip) {
|
||||||
onSizeChangedCallback({
|
onSizeChangedCallback({
|
||||||
_sizeChanged: !hasDirectionCache,
|
_sizeChanged: !hasDirectionCache,
|
||||||
_directionIsRTLCache: hasDirectionCache ? (sizeChangedContext as CacheValues<boolean>) : undefined,
|
_directionIsRTLCache: hasDirectionCache
|
||||||
|
? (sizeChangedContext as CacheValues<boolean>)
|
||||||
|
: undefined,
|
||||||
_appear: !!appear,
|
_appear: !!appear,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const offListeners: (() => void)[] = [];
|
const offListeners: (() => void)[] = [];
|
||||||
let appearCallback: ((...args: any) => any) | false = observeAppearChange ? onSizeChangedCallbackProxy : false;
|
let appearCallback: ((...args: any) => any) | false = observeAppearChange
|
||||||
|
? onSizeChangedCallbackProxy
|
||||||
|
: false;
|
||||||
let directionIsRTLCache: Cache<boolean> | undefined;
|
let directionIsRTLCache: Cache<boolean> | undefined;
|
||||||
|
|
||||||
if (ResizeObserverConstructor) {
|
if (ResizeObserverConstructor) {
|
||||||
@@ -196,7 +217,10 @@ export const createSizeObserver = (
|
|||||||
reset();
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
push(offListeners, [on(expandElement, scrollEventName, onScroll), on(shrinkElement, scrollEventName, onScroll)]);
|
push(offListeners, [
|
||||||
|
on(expandElement, scrollEventName, onScroll),
|
||||||
|
on(shrinkElement, scrollEventName, onScroll),
|
||||||
|
]);
|
||||||
|
|
||||||
// lets assume that the divs will never be that large and a constant value is enough
|
// lets assume that the divs will never be that large and a constant value is enough
|
||||||
style(expandElementChild, {
|
style(expandElementChild, {
|
||||||
@@ -210,17 +234,20 @@ export const createSizeObserver = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (observeDirectionChange) {
|
if (observeDirectionChange) {
|
||||||
directionIsRTLCache = createCache(directionIsRTL.bind(0, sizeObserver));
|
directionIsRTLCache = createCache(getIsDirectionRTL, {
|
||||||
const { _update: updateDirectionIsRTLCache } = directionIsRTLCache;
|
_initialValue: !getIsDirectionRTL(), // invert current value to trigger initial change
|
||||||
|
});
|
||||||
|
const [updateDirectionIsRTLCache] = directionIsRTLCache;
|
||||||
|
|
||||||
push(
|
push(
|
||||||
offListeners,
|
offListeners,
|
||||||
on(sizeObserver, scrollEventName, (event: Event) => {
|
on(sizeObserver, scrollEventName, (event: Event) => {
|
||||||
const directionIsRTLCacheValues = updateDirectionIsRTLCache();
|
const directionIsRTLCacheValues = updateDirectionIsRTLCache();
|
||||||
console.log;
|
const [directionIsRTL, directionIsRTLChanged] = directionIsRTLCacheValues;
|
||||||
const { _value, _changed } = directionIsRTLCacheValues;
|
|
||||||
if (_changed) {
|
if (directionIsRTLChanged) {
|
||||||
removeClass(listenerElement, 'ltr rtl');
|
removeClass(listenerElement, 'ltr rtl');
|
||||||
if (_value) {
|
if (directionIsRTL) {
|
||||||
addClass(listenerElement, 'rtl');
|
addClass(listenerElement, 'rtl');
|
||||||
} else {
|
} else {
|
||||||
addClass(listenerElement, 'ltr');
|
addClass(listenerElement, 'ltr');
|
||||||
@@ -255,12 +282,8 @@ export const createSizeObserver = (
|
|||||||
_getCurrentCacheValues(force?: boolean) {
|
_getCurrentCacheValues(force?: boolean) {
|
||||||
return {
|
return {
|
||||||
_directionIsRTL: directionIsRTLCache
|
_directionIsRTL: directionIsRTLCache
|
||||||
? directionIsRTLCache._current(force)
|
? directionIsRTLCache[1](force) // get current cache values
|
||||||
: {
|
: [false, false, false],
|
||||||
_value: false,
|
|
||||||
_previous: false,
|
|
||||||
_changed: false,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const createTrinsicObserver = (
|
|||||||
): TrinsicObserver => {
|
): TrinsicObserver => {
|
||||||
const trinsicObserver = createDiv(classNameTrinsicObserver);
|
const trinsicObserver = createDiv(classNameTrinsicObserver);
|
||||||
const offListeners: (() => void)[] = [];
|
const offListeners: (() => void)[] = [];
|
||||||
const { _update: updateHeightIntrinsicCache, _current: getCurrentHeightIntrinsicCache } = createCache<
|
const [updateHeightIntrinsicCache, getCurrentHeightIntrinsicCache] = createCache<
|
||||||
boolean,
|
boolean,
|
||||||
IntersectionObserverEntry | WH<number>
|
IntersectionObserverEntry | WH<number>
|
||||||
>(
|
>(
|
||||||
@@ -47,18 +47,24 @@ export const createTrinsicObserver = (
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const triggerOnTrinsicChangedCallback = (
|
||||||
|
updateValue?: IntersectionObserverEntry | WH<number>
|
||||||
|
) => {
|
||||||
|
if (updateValue) {
|
||||||
|
const heightIntrinsic = updateHeightIntrinsicCache(0, updateValue);
|
||||||
|
const [, heightIntrinsicChanged] = heightIntrinsic;
|
||||||
|
|
||||||
|
if (heightIntrinsicChanged) {
|
||||||
|
onTrinsicChangedCallback(heightIntrinsic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (IntersectionObserverConstructor) {
|
if (IntersectionObserverConstructor) {
|
||||||
const intersectionObserverInstance: IntersectionObserver = new IntersectionObserverConstructor(
|
const intersectionObserverInstance: IntersectionObserver = new IntersectionObserverConstructor(
|
||||||
(entries: IntersectionObserverEntry[]) => {
|
(entries: IntersectionObserverEntry[]) => {
|
||||||
if (entries && entries.length > 0) {
|
if (entries && entries.length > 0) {
|
||||||
const last = entries.pop();
|
triggerOnTrinsicChangedCallback(entries.pop());
|
||||||
if (last) {
|
|
||||||
const heightIntrinsic = updateHeightIntrinsicCache(0, last);
|
|
||||||
|
|
||||||
if (heightIntrinsic._changed) {
|
|
||||||
onTrinsicChangedCallback(heightIntrinsic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ root: target }
|
{ root: target }
|
||||||
@@ -70,10 +76,7 @@ export const createTrinsicObserver = (
|
|||||||
} else {
|
} else {
|
||||||
const onSizeChanged = () => {
|
const onSizeChanged = () => {
|
||||||
const newSize = offsetSize(trinsicObserver);
|
const newSize = offsetSize(trinsicObserver);
|
||||||
const heightIntrinsicCache = updateHeightIntrinsicCache(0, newSize);
|
triggerOnTrinsicChangedCallback(newSize);
|
||||||
if (heightIntrinsicCache._changed) {
|
|
||||||
onTrinsicChangedCallback(heightIntrinsicCache);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
push(offListeners, createSizeObserver(trinsicObserver, onSizeChanged)._destroy);
|
push(offListeners, createSizeObserver(trinsicObserver, onSizeChanged)._destroy);
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import { OSOptions, optionsTemplate } from 'options';
|
|||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
|
|
||||||
export interface OverlayScrollbarsStatic {
|
export interface OverlayScrollbarsStatic {
|
||||||
(target: OSTarget | OSInitializationObject, options?: PartialOptions<OSOptions>, extensions?: any): OverlayScrollbars;
|
(
|
||||||
|
target: OSTarget | OSInitializationObject,
|
||||||
|
options?: PartialOptions<OSOptions>,
|
||||||
|
extensions?: any
|
||||||
|
): OverlayScrollbars;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OverlayScrollbars {
|
export interface OverlayScrollbars {
|
||||||
@@ -29,7 +33,8 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
const currentOptions: OSOptions = assignDeep(
|
const currentOptions: OSOptions = assignDeep(
|
||||||
{},
|
{},
|
||||||
_getDefaultOptions(),
|
_getDefaultOptions(),
|
||||||
validateOptions(options || ({} as PartialOptions<OSOptions>), optionsTemplate, null, true)._validated
|
validateOptions(options || ({} as PartialOptions<OSOptions>), optionsTemplate, null, true)
|
||||||
|
._validated
|
||||||
);
|
);
|
||||||
const structureSetup: StructureSetup = createStructureSetup(target);
|
const structureSetup: StructureSetup = createStructureSetup(target);
|
||||||
const scrollbarsSetup: ScrollbarsSetup = createScrollbarsSetup(target, structureSetup);
|
const scrollbarsSetup: ScrollbarsSetup = createScrollbarsSetup(target, structureSetup);
|
||||||
@@ -38,7 +43,12 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
const instance: OverlayScrollbars = {
|
const instance: OverlayScrollbars = {
|
||||||
options(newOptions?: PartialOptions<OSOptions>) {
|
options(newOptions?: PartialOptions<OSOptions>) {
|
||||||
if (newOptions) {
|
if (newOptions) {
|
||||||
const { _validated: _changedOptions } = validateOptions(newOptions, optionsTemplate, currentOptions, true);
|
const { _validated: _changedOptions } = validateOptions(
|
||||||
|
newOptions,
|
||||||
|
optionsTemplate,
|
||||||
|
currentOptions,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
if (!isEmptyObject(_changedOptions)) {
|
if (!isEmptyObject(_changedOptions)) {
|
||||||
assignDeep(currentOptions, _changedOptions);
|
assignDeep(currentOptions, _changedOptions);
|
||||||
|
|||||||
+34
-35
@@ -1,41 +1,44 @@
|
|||||||
export interface CacheValues<T> {
|
export type CacheValues<T> = [
|
||||||
readonly _value?: T;
|
T, // value
|
||||||
readonly _previous?: T;
|
boolean, // changed
|
||||||
_changed: boolean;
|
T | undefined // previous
|
||||||
}
|
];
|
||||||
|
|
||||||
|
export type Cache<Value, Ctx = undefined> = [
|
||||||
|
CacheUpdate<Value, Ctx>,
|
||||||
|
(force?: boolean) => CacheValues<Value> // getCurrent
|
||||||
|
];
|
||||||
|
|
||||||
export interface CacheOptions<T> {
|
export interface CacheOptions<T> {
|
||||||
|
// initial value of _value.
|
||||||
|
_initialValue: T;
|
||||||
// Custom comparison function if shallow compare isn't enough. Returns true if nothing changed.
|
// Custom comparison function if shallow compare isn't enough. Returns true if nothing changed.
|
||||||
_equal?: EqualCachePropFunction<T>;
|
_equal?: EqualCachePropFunction<T>;
|
||||||
// Initial value for _value
|
// If true always updates _value and _previous, otherwise they update only when they changed.
|
||||||
_initialValue?: T;
|
|
||||||
// If true updates always _value and _previous, otherwise they update only when changed
|
|
||||||
_alwaysUpdateValues?: boolean;
|
_alwaysUpdateValues?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Cache<T, C = undefined> {
|
|
||||||
_current: (force?: boolean) => CacheValues<T>;
|
|
||||||
_update: CacheUpdate<T, C>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CacheUpdate<T, C> = undefined extends C
|
export type CacheUpdate<T, C> = undefined extends C
|
||||||
? (force?: boolean | 0, context?: C) => CacheValues<T>
|
? (force?: boolean | 0, context?: C) => CacheValues<T>
|
||||||
: (force: boolean | 0, context: C) => CacheValues<T>;
|
: (force: boolean | 0, context: C) => CacheValues<T>;
|
||||||
|
|
||||||
export type UpdateCachePropFunction<T, C> = undefined extends C
|
export type UpdateCachePropFunction<Value, Ctx> = undefined extends Ctx
|
||||||
? (context?: C, current?: T, previous?: T) => T
|
? (context?: Ctx, current?: Value, previous?: Value) => Value
|
||||||
: C extends T
|
: Ctx extends Value
|
||||||
? ((context: C, current?: T, previous?: T) => T) | 0
|
? ((context: Ctx, current?: Value, previous?: Value) => Value) | 0
|
||||||
: (context: C, current?: T, previous?: T) => T;
|
: (context: Ctx, current?: Value, previous?: Value) => Value;
|
||||||
|
|
||||||
export type EqualCachePropFunction<T> = (currentVal?: T, newVal?: T) => boolean;
|
export type EqualCachePropFunction<T> = (currentVal?: T, newVal?: T) => boolean;
|
||||||
|
|
||||||
export const createCache = <T, C = undefined>(update: UpdateCachePropFunction<T, C>, options?: CacheOptions<T>): Cache<T, C> => {
|
export const createCache = <Value, Ctx = undefined>(
|
||||||
const { _equal, _initialValue, _alwaysUpdateValues } = options || {};
|
update: UpdateCachePropFunction<Value, Ctx>,
|
||||||
let _value: T | undefined = _initialValue;
|
options: CacheOptions<Value>
|
||||||
let _previous: T | undefined;
|
): Cache<Value, Ctx> => {
|
||||||
|
const { _initialValue, _equal, _alwaysUpdateValues } = options || {};
|
||||||
|
let _value: Value = _initialValue;
|
||||||
|
let _previous: Value | undefined;
|
||||||
|
|
||||||
const cacheUpdate = ((force?: boolean | 0, context?: C) => {
|
const cacheUpdate = ((force?: boolean | 0, context?: Ctx) => {
|
||||||
const curr = _value;
|
const curr = _value;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// update can only not be a function if C extends T as described in "UpdateCachePropFunction" type definition
|
// update can only not be a function if C extends T as described in "UpdateCachePropFunction" type definition
|
||||||
@@ -48,19 +51,15 @@ export const createCache = <T, C = undefined>(update: UpdateCachePropFunction<T,
|
|||||||
_previous = curr;
|
_previous = curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return [_value, changed, _previous];
|
||||||
_value,
|
}) as CacheUpdate<Value, Ctx>;
|
||||||
_previous,
|
|
||||||
_changed: changed,
|
|
||||||
};
|
|
||||||
}) as CacheUpdate<T, C>;
|
|
||||||
|
|
||||||
return {
|
return [
|
||||||
_update: cacheUpdate,
|
cacheUpdate,
|
||||||
_current: (force?: boolean) => ({
|
(force?: boolean) => [
|
||||||
_value,
|
_value,
|
||||||
|
!!force, // changed
|
||||||
_previous,
|
_previous,
|
||||||
_changed: !!force,
|
],
|
||||||
}),
|
];
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
import 'jest-playwright-preset';
|
|
||||||
import 'expect-playwright';
|
|
||||||
import url from './.build/build.html';
|
|
||||||
|
|
||||||
describe('StructureLifecycle', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await jestPlaywright.resetPage();
|
|
||||||
await page.goto(url);
|
|
||||||
});
|
|
||||||
|
|
||||||
[false, true].forEach(async (nativeScrollbarStyling) => {
|
|
||||||
const withText = nativeScrollbarStyling ? 'with' : 'without';
|
|
||||||
const nss = async () => {
|
|
||||||
if (!nativeScrollbarStyling) {
|
|
||||||
await page.click('#nss');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
describe(`structureLifecycles ${withText} native scrollbar styling`, () => {
|
|
||||||
test('default', async () => {
|
|
||||||
await nss();
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('without flexbox glue & css custom props', async () => {
|
|
||||||
await nss();
|
|
||||||
await page.click('#fbg');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click('#ccp');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
|
|
||||||
// firefox can't simulate partially overlaid scrollbars, boost speed by omitting webkit
|
|
||||||
test.jestPlaywrightSkip({ browsers: ['firefox', 'webkit'] }, 'with partially overlaid scrollbars', async () => {
|
|
||||||
await nss();
|
|
||||||
await page.click('#po');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('with fully overlaid scrollbars', async () => {
|
|
||||||
await nss();
|
|
||||||
await page.click('#fo');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import 'jest-playwright-preset';
|
|
||||||
import 'expect-playwright';
|
|
||||||
import url from './.build/build.html';
|
|
||||||
|
|
||||||
describe('DOMObserver', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await jestPlaywright.resetPage();
|
|
||||||
await page.goto(url);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('test', async () => {
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import 'jest-playwright-preset';
|
|
||||||
import 'expect-playwright';
|
|
||||||
import url from './.build/build.html';
|
|
||||||
|
|
||||||
describe('SizeObserver', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await jestPlaywright.resetPage();
|
|
||||||
await page.goto(url);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('with ResizeOserver', async () => {
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('with ResizeOserver polyfill', async () => {
|
|
||||||
await page.click('#roPolyfill');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import 'jest-playwright-preset';
|
|
||||||
import 'expect-playwright';
|
|
||||||
import url from './.build/build.html';
|
|
||||||
|
|
||||||
describe('TrinsicObserver', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await jestPlaywright.resetPage();
|
|
||||||
await page.goto(url);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('with IntersectionObserver', async () => {
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('with ResizeObserver', async () => {
|
|
||||||
await page.click('#ioPolyfill');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('with ResizeObserver polyfill', async () => {
|
|
||||||
await page.click('#ioPolyfill');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click('#roPolyfill');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.click('#start');
|
|
||||||
await expect(page).toHaveSelector('#testResult.passed');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,902 @@
|
|||||||
|
import {
|
||||||
|
Environment,
|
||||||
|
StructureInitializationStaticElement,
|
||||||
|
StructureInitializationDynamicElement,
|
||||||
|
} from 'environment';
|
||||||
|
import { OSTarget, StructureInitialization } from 'typings';
|
||||||
|
import { createStructureSetup, StructureSetup } from 'setups/structureSetup';
|
||||||
|
import { isHTMLElement } from 'support';
|
||||||
|
|
||||||
|
const mockGetEnvironment = jest.fn();
|
||||||
|
jest.mock('environment', () => {
|
||||||
|
return {
|
||||||
|
getEnvironment: jest.fn().mockImplementation(() => mockGetEnvironment()),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
interface StructureSetupProxy {
|
||||||
|
input: OSTarget | StructureInitialization;
|
||||||
|
setup: StructureSetup;
|
||||||
|
}
|
||||||
|
|
||||||
|
const textareaId = 'textarea';
|
||||||
|
const textareaHostId = 'host';
|
||||||
|
const elementId = 'target';
|
||||||
|
const dynamicContent = 'text<p>paragraph</p>';
|
||||||
|
const textareaContent = `<textarea id="${textareaId}">text</textarea>`;
|
||||||
|
const getSnapshot = () => document.body.innerHTML;
|
||||||
|
const getTarget = (textarea?: boolean) =>
|
||||||
|
document.getElementById(textarea ? textareaId : elementId)!;
|
||||||
|
const fillBody = (textarea?: boolean, customDOM?: (content: string, hostId: string) => string) => {
|
||||||
|
document.body.innerHTML = `
|
||||||
|
<nav></nav>
|
||||||
|
${
|
||||||
|
customDOM
|
||||||
|
? customDOM(
|
||||||
|
textarea ? textareaContent : dynamicContent,
|
||||||
|
textarea ? textareaHostId : elementId
|
||||||
|
)
|
||||||
|
: textarea
|
||||||
|
? textareaContent
|
||||||
|
: `<div id="${elementId}">${dynamicContent}</div>`
|
||||||
|
}
|
||||||
|
<footer></footer>
|
||||||
|
`;
|
||||||
|
return getSnapshot();
|
||||||
|
};
|
||||||
|
const clearBody = () => {
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getElements = (textarea?: boolean) => {
|
||||||
|
const target = getTarget(textarea);
|
||||||
|
const host = document.querySelector('.os-host')!;
|
||||||
|
const padding = document.querySelector('.os-padding')!;
|
||||||
|
const viewport = document.querySelector('.os-viewport')!;
|
||||||
|
const content = document.querySelector('.os-content')!;
|
||||||
|
|
||||||
|
return {
|
||||||
|
target,
|
||||||
|
host,
|
||||||
|
padding,
|
||||||
|
viewport,
|
||||||
|
content,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertCorrectDOMStructure = (textarea?: boolean) => {
|
||||||
|
const { target, host, padding, viewport, content } = getElements(textarea);
|
||||||
|
|
||||||
|
expect(host).toBeTruthy();
|
||||||
|
expect(viewport).toBeTruthy();
|
||||||
|
expect(viewport.parentElement).toBe(padding || host);
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
expect(content.parentElement).toBe(viewport);
|
||||||
|
}
|
||||||
|
if (padding) {
|
||||||
|
expect(padding.parentElement).toBe(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(host.parentElement).toBe(document.body);
|
||||||
|
expect(host.previousElementSibling).toBe(document.querySelector('nav'));
|
||||||
|
expect(host.nextElementSibling).toBe(document.querySelector('footer'));
|
||||||
|
|
||||||
|
const contentElm = content || viewport;
|
||||||
|
if (textarea) {
|
||||||
|
expect(target.parentElement).toBe(contentElm);
|
||||||
|
expect(contentElm.innerHTML).toBe(textareaContent);
|
||||||
|
} else {
|
||||||
|
expect(target).toBe(host);
|
||||||
|
expect(contentElm.innerHTML).toBe(dynamicContent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createStructureSetupProxy = (
|
||||||
|
target: OSTarget | StructureInitialization
|
||||||
|
): StructureSetupProxy => ({
|
||||||
|
input: target,
|
||||||
|
setup: createStructureSetup(target),
|
||||||
|
});
|
||||||
|
|
||||||
|
const assertCorrectSetup = (
|
||||||
|
textarea: boolean,
|
||||||
|
setupProxy: StructureSetupProxy,
|
||||||
|
environment: Environment
|
||||||
|
): StructureSetup => {
|
||||||
|
const { input, setup } = setupProxy;
|
||||||
|
const { _targetObj, _targetCtx, _destroy } = setup;
|
||||||
|
const { _target, _host, _padding, _viewport, _content } = _targetObj;
|
||||||
|
const { target, host, padding, viewport, content } = getElements(textarea);
|
||||||
|
const isTextarea = target.matches('textarea');
|
||||||
|
const isBody = target.matches('body');
|
||||||
|
|
||||||
|
expect(textarea).toBe(isTextarea);
|
||||||
|
|
||||||
|
expect(_target).toBe(target);
|
||||||
|
expect(_host).toBe(host);
|
||||||
|
|
||||||
|
if (padding || _padding) {
|
||||||
|
expect(_padding).toBe(padding);
|
||||||
|
} else {
|
||||||
|
expect(padding).toBeFalsy();
|
||||||
|
expect(_padding).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewport || _viewport) {
|
||||||
|
expect(_viewport).toBe(viewport);
|
||||||
|
} else {
|
||||||
|
expect(viewport).toBeFalsy();
|
||||||
|
expect(_viewport).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content || _content) {
|
||||||
|
expect(_content).toBe(content);
|
||||||
|
} else {
|
||||||
|
expect(content).toBeFalsy();
|
||||||
|
expect(_content).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { _isTextarea, _isBody, _bodyElm, _htmlElm, _documentElm, _windowElm } = _targetCtx;
|
||||||
|
|
||||||
|
expect(_isTextarea).toBe(isTextarea);
|
||||||
|
expect(_isBody).toBe(isBody);
|
||||||
|
expect(_windowElm).toBe(document.defaultView);
|
||||||
|
expect(_documentElm).toBe(document);
|
||||||
|
expect(_htmlElm).toBe(document.body.parentElement);
|
||||||
|
expect(_bodyElm).toBe(document.body);
|
||||||
|
|
||||||
|
expect(typeof _destroy).toBe('function');
|
||||||
|
|
||||||
|
const { _nativeScrollbarStyling, _cssCustomProperties, _getInitializationStrategy } = environment;
|
||||||
|
const {
|
||||||
|
_host: hostInitStrategy,
|
||||||
|
_viewport: viewportInitStrategy,
|
||||||
|
_padding: paddingInitStrategy,
|
||||||
|
_content: contentInitStrategy,
|
||||||
|
} = _getInitializationStrategy();
|
||||||
|
const inputIsElement = isHTMLElement(input);
|
||||||
|
const inputAsObj = input as StructureInitialization;
|
||||||
|
const styleElm = document.querySelector('style');
|
||||||
|
const checkStrategyDependendElements = (
|
||||||
|
elm: Element | null,
|
||||||
|
input: HTMLElement | boolean | undefined,
|
||||||
|
isStaticStrategy: boolean,
|
||||||
|
strategy: StructureInitializationStaticElement | StructureInitializationDynamicElement,
|
||||||
|
id: string
|
||||||
|
) => {
|
||||||
|
if (input) {
|
||||||
|
expect(elm).toBeTruthy();
|
||||||
|
} else {
|
||||||
|
if (input === false) {
|
||||||
|
expect(elm).toBeFalsy();
|
||||||
|
}
|
||||||
|
if (input === undefined) {
|
||||||
|
if (isStaticStrategy) {
|
||||||
|
strategy = strategy as StructureInitializationStaticElement;
|
||||||
|
if (typeof strategy === 'function') {
|
||||||
|
const result = strategy(target);
|
||||||
|
if (result) {
|
||||||
|
expect(result).toBe(elm);
|
||||||
|
} else {
|
||||||
|
expect(elm).toBeTruthy();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expect(elm).toBeTruthy();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
strategy = strategy as StructureInitializationDynamicElement;
|
||||||
|
const expectDefaultValue = () => {
|
||||||
|
if (id === 'padding') {
|
||||||
|
if (_nativeScrollbarStyling) {
|
||||||
|
expect(elm).toBeFalsy();
|
||||||
|
} else {
|
||||||
|
expect(elm).toBeTruthy();
|
||||||
|
}
|
||||||
|
} else if (id === 'content') {
|
||||||
|
expect(elm).toBeFalsy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (typeof strategy === 'function') {
|
||||||
|
const result = strategy(target);
|
||||||
|
const resultIsBoolean = typeof result === 'boolean';
|
||||||
|
if (resultIsBoolean) {
|
||||||
|
if (result) {
|
||||||
|
expect(elm).toBeTruthy();
|
||||||
|
} else {
|
||||||
|
expect(elm).toBeFalsy();
|
||||||
|
}
|
||||||
|
} else if (result) {
|
||||||
|
expect(elm).toBe(result);
|
||||||
|
} else {
|
||||||
|
expectDefaultValue();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const strategyIsBoolean = typeof strategy === 'boolean';
|
||||||
|
if (strategyIsBoolean) {
|
||||||
|
if (strategy) {
|
||||||
|
expect(elm).toBeTruthy();
|
||||||
|
} else {
|
||||||
|
expect(elm).toBeFalsy();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expectDefaultValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_nativeScrollbarStyling || _cssCustomProperties) {
|
||||||
|
expect(styleElm).toBeFalsy();
|
||||||
|
} else {
|
||||||
|
expect(styleElm).toBeTruthy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputIsElement) {
|
||||||
|
checkStrategyDependendElements(padding, undefined, false, paddingInitStrategy, 'padding');
|
||||||
|
checkStrategyDependendElements(content, undefined, false, contentInitStrategy, 'content');
|
||||||
|
checkStrategyDependendElements(viewport, undefined, true, viewportInitStrategy, 'viewport');
|
||||||
|
checkStrategyDependendElements(host, undefined, true, hostInitStrategy, 'host');
|
||||||
|
} else {
|
||||||
|
const {
|
||||||
|
padding: inputPadding,
|
||||||
|
content: inputContent,
|
||||||
|
viewport: inputViewport,
|
||||||
|
host: inputHost,
|
||||||
|
} = inputAsObj;
|
||||||
|
checkStrategyDependendElements(padding, inputPadding, false, paddingInitStrategy, 'padding');
|
||||||
|
checkStrategyDependendElements(content, inputContent, false, contentInitStrategy, 'content');
|
||||||
|
checkStrategyDependendElements(viewport, inputViewport, true, viewportInitStrategy, 'viewport');
|
||||||
|
checkStrategyDependendElements(host, inputHost, true, hostInitStrategy, 'host');
|
||||||
|
}
|
||||||
|
|
||||||
|
return setup;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertCorrectDestroy = (snapshot: string, setup: StructureSetup) => {
|
||||||
|
const { _destroy } = setup;
|
||||||
|
|
||||||
|
_destroy();
|
||||||
|
|
||||||
|
// remove empty class attr
|
||||||
|
const elms = document.querySelectorAll('*');
|
||||||
|
Array.from(elms).forEach((elm) => {
|
||||||
|
const classAttr = elm.getAttribute('class');
|
||||||
|
if (classAttr === '') {
|
||||||
|
elm.removeAttribute('class');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(snapshot).toBe(getSnapshot());
|
||||||
|
};
|
||||||
|
|
||||||
|
const env: Environment = jest.requireActual('environment').getEnvironment();
|
||||||
|
const envDefault = {
|
||||||
|
name: 'default',
|
||||||
|
env,
|
||||||
|
};
|
||||||
|
const envNativeScrollbarStyling = {
|
||||||
|
name: 'native scrollbar styling',
|
||||||
|
env: {
|
||||||
|
...env,
|
||||||
|
_nativeScrollbarStyling: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const envCssCustomProperties = {
|
||||||
|
name: 'custom css properties',
|
||||||
|
env: {
|
||||||
|
...env,
|
||||||
|
_cssCustomProperties: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const envInitStrategyMin = {
|
||||||
|
name: 'initialization strategy min',
|
||||||
|
env: {
|
||||||
|
...env,
|
||||||
|
_getInitializationStrategy: () => ({
|
||||||
|
_host: null,
|
||||||
|
_viewport: () => null,
|
||||||
|
_content: () => false,
|
||||||
|
_padding: false,
|
||||||
|
_scrollbarsSlot: null,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const envInitStrategyMax = {
|
||||||
|
name: 'initialization strategy max',
|
||||||
|
env: {
|
||||||
|
...env,
|
||||||
|
_getInitializationStrategy: () => ({
|
||||||
|
_host: null,
|
||||||
|
_viewport: null,
|
||||||
|
_content: true,
|
||||||
|
_padding: () => true,
|
||||||
|
_scrollbarsSlot: null,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const envInitStrategyAssigned = {
|
||||||
|
name: 'initialization strategy assigned',
|
||||||
|
env: {
|
||||||
|
...env,
|
||||||
|
_getInitializationStrategy: () => ({
|
||||||
|
_host: () => document.querySelector('#host1') as HTMLElement,
|
||||||
|
_viewport: (target: HTMLElement) => target.querySelector('#viewport') as HTMLElement,
|
||||||
|
_content: (target: HTMLElement) => target.querySelector('#content') as HTMLElement,
|
||||||
|
_padding: (target: HTMLElement) => target.querySelector('#padding') as HTMLElement,
|
||||||
|
_scrollbarsSlot: null,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('structureSetup', () => {
|
||||||
|
afterEach(() => clearBody());
|
||||||
|
|
||||||
|
[
|
||||||
|
envDefault,
|
||||||
|
envNativeScrollbarStyling,
|
||||||
|
envCssCustomProperties,
|
||||||
|
envInitStrategyMin,
|
||||||
|
envInitStrategyMax,
|
||||||
|
envInitStrategyAssigned,
|
||||||
|
].forEach((envWithName) => {
|
||||||
|
const { env: currEnv, name } = envWithName;
|
||||||
|
describe(`Environment: ${name}`, () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
mockGetEnvironment.mockImplementation(() => currEnv);
|
||||||
|
});
|
||||||
|
|
||||||
|
[false, true].forEach((isTextarea) => {
|
||||||
|
describe(isTextarea ? 'textarea' : 'element', () => {
|
||||||
|
describe('basic', () => {
|
||||||
|
test('Element', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy(getTarget(isTextarea)),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Object', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({ target: getTarget(isTextarea) }),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('complex', () => {
|
||||||
|
describe('single assigned', () => {
|
||||||
|
test('padding', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multiple assigned', () => {
|
||||||
|
test('padding viewport content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding"><div id="viewport"><div id="content">${content}</div></div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('padding viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('padding content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding"><div id="content">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('viewport content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('single false', () => {
|
||||||
|
test('padding', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: false,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
content: false,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('single true', () => {
|
||||||
|
test('padding', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: true,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
content: true,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multiple false', () => {
|
||||||
|
test('padding & content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: false,
|
||||||
|
content: false,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multiple true', () => {
|
||||||
|
test('padding & content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: true,
|
||||||
|
content: true,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mixed', () => {
|
||||||
|
test('false: padding & content | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: false,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: false,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true: padding & content | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: true,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: true,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true: content | false: padding | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: false,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: true,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true: padding | false: content | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: true,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: false,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('false: padding | assigned: content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: false,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true: padding | assigned: content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: true,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('false: padding | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: false,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true: padding | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: true,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('false: padding | assigned: viewport & content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
padding: false,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true: padding | assigned: viewport & content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
padding: true,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('false: content | assigned: padding', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
content: false,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true: content | assigned: padding', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
content: true,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('false: content | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: false,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true: content | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: true,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('false: content | assigned: padding & viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: false,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true: content | assigned: padding & viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetupProxy({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: true,
|
||||||
|
}),
|
||||||
|
currEnv
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,870 +0,0 @@
|
|||||||
import { Environment, StructureInitializationStaticElement, StructureInitializationDynamicElement } from 'environment';
|
|
||||||
import { OSTarget, StructureInitialization } from 'typings';
|
|
||||||
import { createStructureSetup, StructureSetup } from 'setups/structureSetup';
|
|
||||||
import { isHTMLElement } from 'support';
|
|
||||||
|
|
||||||
const mockGetEnvironment = jest.fn();
|
|
||||||
jest.mock('environment', () => {
|
|
||||||
return {
|
|
||||||
getEnvironment: jest.fn().mockImplementation(() => mockGetEnvironment()),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
interface StructureSetupProxy {
|
|
||||||
input: OSTarget | StructureInitialization;
|
|
||||||
setup: StructureSetup;
|
|
||||||
}
|
|
||||||
|
|
||||||
const textareaId = 'textarea';
|
|
||||||
const textareaHostId = 'host';
|
|
||||||
const elementId = 'target';
|
|
||||||
const dynamicContent = 'text<p>paragraph</p>';
|
|
||||||
const textareaContent = `<textarea id="${textareaId}">text</textarea>`;
|
|
||||||
const getSnapshot = () => document.body.innerHTML;
|
|
||||||
const getTarget = (textarea?: boolean) => document.getElementById(textarea ? textareaId : elementId)!;
|
|
||||||
const fillBody = (textarea?: boolean, customDOM?: (content: string, hostId: string) => string) => {
|
|
||||||
document.body.innerHTML = `
|
|
||||||
<nav></nav>
|
|
||||||
${
|
|
||||||
customDOM
|
|
||||||
? customDOM(textarea ? textareaContent : dynamicContent, textarea ? textareaHostId : elementId)
|
|
||||||
: textarea
|
|
||||||
? textareaContent
|
|
||||||
: `<div id="${elementId}">${dynamicContent}</div>`
|
|
||||||
}
|
|
||||||
<footer></footer>
|
|
||||||
`;
|
|
||||||
return getSnapshot();
|
|
||||||
};
|
|
||||||
const clearBody = () => {
|
|
||||||
document.body.innerHTML = '';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getElements = (textarea?: boolean) => {
|
|
||||||
const target = getTarget(textarea);
|
|
||||||
const host = document.querySelector('.os-host')!;
|
|
||||||
const padding = document.querySelector('.os-padding')!;
|
|
||||||
const viewport = document.querySelector('.os-viewport')!;
|
|
||||||
const content = document.querySelector('.os-content')!;
|
|
||||||
|
|
||||||
return {
|
|
||||||
target,
|
|
||||||
host,
|
|
||||||
padding,
|
|
||||||
viewport,
|
|
||||||
content,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const assertCorrectDOMStructure = (textarea?: boolean) => {
|
|
||||||
const { target, host, padding, viewport, content } = getElements(textarea);
|
|
||||||
|
|
||||||
expect(host).toBeTruthy();
|
|
||||||
expect(viewport).toBeTruthy();
|
|
||||||
expect(viewport.parentElement).toBe(padding || host);
|
|
||||||
|
|
||||||
if (content) {
|
|
||||||
expect(content.parentElement).toBe(viewport);
|
|
||||||
}
|
|
||||||
if (padding) {
|
|
||||||
expect(padding.parentElement).toBe(host);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(host.parentElement).toBe(document.body);
|
|
||||||
expect(host.previousElementSibling).toBe(document.querySelector('nav'));
|
|
||||||
expect(host.nextElementSibling).toBe(document.querySelector('footer'));
|
|
||||||
|
|
||||||
const contentElm = content || viewport;
|
|
||||||
if (textarea) {
|
|
||||||
expect(target.parentElement).toBe(contentElm);
|
|
||||||
expect(contentElm.innerHTML).toBe(textareaContent);
|
|
||||||
} else {
|
|
||||||
expect(target).toBe(host);
|
|
||||||
expect(contentElm.innerHTML).toBe(dynamicContent);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const createStructureSetupProxy = (target: OSTarget | StructureInitialization): StructureSetupProxy => ({
|
|
||||||
input: target,
|
|
||||||
setup: createStructureSetup(target),
|
|
||||||
});
|
|
||||||
|
|
||||||
const assertCorrectSetup = (textarea: boolean, setupProxy: StructureSetupProxy, environment: Environment): StructureSetup => {
|
|
||||||
const { input, setup } = setupProxy;
|
|
||||||
const { _targetObj, _targetCtx, _destroy } = setup;
|
|
||||||
const { _target, _host, _padding, _viewport, _content } = _targetObj;
|
|
||||||
const { target, host, padding, viewport, content } = getElements(textarea);
|
|
||||||
const isTextarea = target.matches('textarea');
|
|
||||||
const isBody = target.matches('body');
|
|
||||||
|
|
||||||
expect(textarea).toBe(isTextarea);
|
|
||||||
|
|
||||||
expect(_target).toBe(target);
|
|
||||||
expect(_host).toBe(host);
|
|
||||||
|
|
||||||
if (padding || _padding) {
|
|
||||||
expect(_padding).toBe(padding);
|
|
||||||
} else {
|
|
||||||
expect(padding).toBeFalsy();
|
|
||||||
expect(_padding).toBeFalsy();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (viewport || _viewport) {
|
|
||||||
expect(_viewport).toBe(viewport);
|
|
||||||
} else {
|
|
||||||
expect(viewport).toBeFalsy();
|
|
||||||
expect(_viewport).toBeFalsy();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content || _content) {
|
|
||||||
expect(_content).toBe(content);
|
|
||||||
} else {
|
|
||||||
expect(content).toBeFalsy();
|
|
||||||
expect(_content).toBeFalsy();
|
|
||||||
}
|
|
||||||
|
|
||||||
const { _isTextarea, _isBody, _bodyElm, _htmlElm, _documentElm, _windowElm } = _targetCtx;
|
|
||||||
|
|
||||||
expect(_isTextarea).toBe(isTextarea);
|
|
||||||
expect(_isBody).toBe(isBody);
|
|
||||||
expect(_windowElm).toBe(document.defaultView);
|
|
||||||
expect(_documentElm).toBe(document);
|
|
||||||
expect(_htmlElm).toBe(document.body.parentElement);
|
|
||||||
expect(_bodyElm).toBe(document.body);
|
|
||||||
|
|
||||||
expect(typeof _destroy).toBe('function');
|
|
||||||
|
|
||||||
const { _nativeScrollbarStyling, _cssCustomProperties, _getInitializationStrategy } = environment;
|
|
||||||
const {
|
|
||||||
_host: hostInitStrategy,
|
|
||||||
_viewport: viewportInitStrategy,
|
|
||||||
_padding: paddingInitStrategy,
|
|
||||||
_content: contentInitStrategy,
|
|
||||||
} = _getInitializationStrategy();
|
|
||||||
const inputIsElement = isHTMLElement(input);
|
|
||||||
const inputAsObj = input as StructureInitialization;
|
|
||||||
const styleElm = document.querySelector('style');
|
|
||||||
const checkStrategyDependendElements = (
|
|
||||||
elm: Element | null,
|
|
||||||
input: HTMLElement | boolean | undefined,
|
|
||||||
isStaticStrategy: boolean,
|
|
||||||
strategy: StructureInitializationStaticElement | StructureInitializationDynamicElement,
|
|
||||||
id: string
|
|
||||||
) => {
|
|
||||||
if (input) {
|
|
||||||
expect(elm).toBeTruthy();
|
|
||||||
} else {
|
|
||||||
if (input === false) {
|
|
||||||
expect(elm).toBeFalsy();
|
|
||||||
}
|
|
||||||
if (input === undefined) {
|
|
||||||
if (isStaticStrategy) {
|
|
||||||
strategy = strategy as StructureInitializationStaticElement;
|
|
||||||
if (typeof strategy === 'function') {
|
|
||||||
const result = strategy(target);
|
|
||||||
if (result) {
|
|
||||||
expect(result).toBe(elm);
|
|
||||||
} else {
|
|
||||||
expect(elm).toBeTruthy();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expect(elm).toBeTruthy();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
strategy = strategy as StructureInitializationDynamicElement;
|
|
||||||
const expectDefaultValue = () => {
|
|
||||||
if (id === 'padding') {
|
|
||||||
if (_nativeScrollbarStyling) {
|
|
||||||
expect(elm).toBeFalsy();
|
|
||||||
} else {
|
|
||||||
expect(elm).toBeTruthy();
|
|
||||||
}
|
|
||||||
} else if (id === 'content') {
|
|
||||||
expect(elm).toBeFalsy();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (typeof strategy === 'function') {
|
|
||||||
const result = strategy(target);
|
|
||||||
const resultIsBoolean = typeof result === 'boolean';
|
|
||||||
if (resultIsBoolean) {
|
|
||||||
if (result) {
|
|
||||||
expect(elm).toBeTruthy();
|
|
||||||
} else {
|
|
||||||
expect(elm).toBeFalsy();
|
|
||||||
}
|
|
||||||
} else if (result) {
|
|
||||||
expect(elm).toBe(result);
|
|
||||||
} else {
|
|
||||||
expectDefaultValue();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const strategyIsBoolean = typeof strategy === 'boolean';
|
|
||||||
if (strategyIsBoolean) {
|
|
||||||
if (strategy) {
|
|
||||||
expect(elm).toBeTruthy();
|
|
||||||
} else {
|
|
||||||
expect(elm).toBeFalsy();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expectDefaultValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_nativeScrollbarStyling || _cssCustomProperties) {
|
|
||||||
expect(styleElm).toBeFalsy();
|
|
||||||
} else {
|
|
||||||
expect(styleElm).toBeTruthy();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputIsElement) {
|
|
||||||
checkStrategyDependendElements(padding, undefined, false, paddingInitStrategy, 'padding');
|
|
||||||
checkStrategyDependendElements(content, undefined, false, contentInitStrategy, 'content');
|
|
||||||
checkStrategyDependendElements(viewport, undefined, true, viewportInitStrategy, 'viewport');
|
|
||||||
checkStrategyDependendElements(host, undefined, true, hostInitStrategy, 'host');
|
|
||||||
} else {
|
|
||||||
const { padding: inputPadding, content: inputContent, viewport: inputViewport, host: inputHost } = inputAsObj;
|
|
||||||
checkStrategyDependendElements(padding, inputPadding, false, paddingInitStrategy, 'padding');
|
|
||||||
checkStrategyDependendElements(content, inputContent, false, contentInitStrategy, 'content');
|
|
||||||
checkStrategyDependendElements(viewport, inputViewport, true, viewportInitStrategy, 'viewport');
|
|
||||||
checkStrategyDependendElements(host, inputHost, true, hostInitStrategy, 'host');
|
|
||||||
}
|
|
||||||
|
|
||||||
return setup;
|
|
||||||
};
|
|
||||||
|
|
||||||
const assertCorrectDestroy = (snapshot: string, setup: StructureSetup) => {
|
|
||||||
const { _destroy } = setup;
|
|
||||||
|
|
||||||
_destroy();
|
|
||||||
|
|
||||||
// remove empty class attr
|
|
||||||
const elms = document.querySelectorAll('*');
|
|
||||||
Array.from(elms).forEach((elm) => {
|
|
||||||
const classAttr = elm.getAttribute('class');
|
|
||||||
if (classAttr === '') {
|
|
||||||
elm.removeAttribute('class');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(snapshot).toBe(getSnapshot());
|
|
||||||
};
|
|
||||||
|
|
||||||
const env: Environment = jest.requireActual('environment').getEnvironment();
|
|
||||||
const envDefault = {
|
|
||||||
name: 'default',
|
|
||||||
env: env,
|
|
||||||
};
|
|
||||||
const envNativeScrollbarStyling = {
|
|
||||||
name: 'native scrollbar styling',
|
|
||||||
env: {
|
|
||||||
...env,
|
|
||||||
_nativeScrollbarStyling: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const envCssCustomProperties = {
|
|
||||||
name: 'custom css properties',
|
|
||||||
env: {
|
|
||||||
...env,
|
|
||||||
_cssCustomProperties: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const envInitStrategyMin = {
|
|
||||||
name: 'initialization strategy min',
|
|
||||||
env: {
|
|
||||||
...env,
|
|
||||||
_getInitializationStrategy: () => ({
|
|
||||||
_host: null,
|
|
||||||
_viewport: () => null,
|
|
||||||
_content: () => false,
|
|
||||||
_padding: false,
|
|
||||||
_scrollbarsSlot: null,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const envInitStrategyMax = {
|
|
||||||
name: 'initialization strategy max',
|
|
||||||
env: {
|
|
||||||
...env,
|
|
||||||
_getInitializationStrategy: () => ({
|
|
||||||
_host: null,
|
|
||||||
_viewport: null,
|
|
||||||
_content: true,
|
|
||||||
_padding: () => true,
|
|
||||||
_scrollbarsSlot: null,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const envInitStrategyAssigned = {
|
|
||||||
name: 'initialization strategy assigned',
|
|
||||||
env: {
|
|
||||||
...env,
|
|
||||||
_getInitializationStrategy: () => ({
|
|
||||||
_host: () => document.querySelector('#host1') as HTMLElement,
|
|
||||||
_viewport: (target: HTMLElement) => target.querySelector('#viewport') as HTMLElement,
|
|
||||||
_content: (target: HTMLElement) => target.querySelector('#content') as HTMLElement,
|
|
||||||
_padding: (target: HTMLElement) => target.querySelector('#padding') as HTMLElement,
|
|
||||||
_scrollbarsSlot: null,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('structureSetup', () => {
|
|
||||||
afterEach(() => clearBody());
|
|
||||||
|
|
||||||
[envDefault, envNativeScrollbarStyling, envCssCustomProperties, envInitStrategyMin, envInitStrategyMax, envInitStrategyAssigned].forEach(
|
|
||||||
(envWithName) => {
|
|
||||||
const { env: currEnv, name } = envWithName;
|
|
||||||
describe(`Environment: ${name}`, () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
mockGetEnvironment.mockImplementation(() => currEnv);
|
|
||||||
});
|
|
||||||
|
|
||||||
[false, true].forEach((isTextarea) => {
|
|
||||||
describe(isTextarea ? 'textarea' : 'element', () => {
|
|
||||||
describe('basic', () => {
|
|
||||||
test('Element', () => {
|
|
||||||
const snapshot = fillBody(isTextarea);
|
|
||||||
const setup = assertCorrectSetup(isTextarea, createStructureSetupProxy(getTarget(isTextarea)), currEnv);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Object', () => {
|
|
||||||
const snapshot = fillBody(isTextarea);
|
|
||||||
const setup = assertCorrectSetup(isTextarea, createStructureSetupProxy({ target: getTarget(isTextarea) }), currEnv);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('complex', () => {
|
|
||||||
describe('single assigned', () => {
|
|
||||||
test('padding', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: document.querySelector<HTMLElement>('#padding')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
content: document.querySelector<HTMLElement>('#content')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('multiple assigned', () => {
|
|
||||||
test('padding viewport content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="padding"><div id="viewport"><div id="content">${content}</div></div></div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: document.querySelector<HTMLElement>('#padding')!,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: document.querySelector<HTMLElement>('#content')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('padding viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: document.querySelector<HTMLElement>('#padding')!,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('padding content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="padding"><div id="content">${content}</div></div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: document.querySelector<HTMLElement>('#padding')!,
|
|
||||||
content: document.querySelector<HTMLElement>('#content')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('viewport content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: document.querySelector<HTMLElement>('#content')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('single false', () => {
|
|
||||||
test('padding', () => {
|
|
||||||
const snapshot = fillBody(isTextarea);
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: false,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea);
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
content: false,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('single true', () => {
|
|
||||||
test('padding', () => {
|
|
||||||
const snapshot = fillBody(isTextarea);
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: true,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea);
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
content: true,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('multiple false', () => {
|
|
||||||
test('padding & content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea);
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: false,
|
|
||||||
content: false,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('multiple true', () => {
|
|
||||||
test('padding & content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea);
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: true,
|
|
||||||
content: true,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('mixed', () => {
|
|
||||||
test('false: padding & content | assigned: viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: false,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: false,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('true: padding & content | assigned: viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: true,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: true,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('true: content | false: padding | assigned: viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: false,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: true,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('true: padding | false: content | assigned: viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: true,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: false,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('false: padding | assigned: content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: false,
|
|
||||||
content: document.querySelector<HTMLElement>('#content')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('true: padding | assigned: content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: true,
|
|
||||||
content: document.querySelector<HTMLElement>('#content')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('false: padding | assigned: viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: false,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('true: padding | assigned: viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: true,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('false: padding | assigned: viewport & content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
padding: false,
|
|
||||||
content: document.querySelector<HTMLElement>('#content')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('true: padding | assigned: viewport & content', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
padding: true,
|
|
||||||
content: document.querySelector<HTMLElement>('#content')!,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('false: content | assigned: padding', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: document.querySelector<HTMLElement>('#padding')!,
|
|
||||||
content: false,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('true: content | assigned: padding', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: document.querySelector<HTMLElement>('#padding')!,
|
|
||||||
content: true,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('false: content | assigned: viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: false,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('true: content | assigned: viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: true,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('false: content | assigned: padding & viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: document.querySelector<HTMLElement>('#padding')!,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: false,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('true: content | assigned: padding & viewport', () => {
|
|
||||||
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
|
||||||
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
|
|
||||||
});
|
|
||||||
const setup = assertCorrectSetup(
|
|
||||||
isTextarea,
|
|
||||||
createStructureSetupProxy({
|
|
||||||
host: document.querySelector<HTMLElement>('#host')!,
|
|
||||||
target: getTarget(isTextarea),
|
|
||||||
padding: document.querySelector<HTMLElement>('#padding')!,
|
|
||||||
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
|
||||||
content: true,
|
|
||||||
}),
|
|
||||||
currEnv
|
|
||||||
);
|
|
||||||
assertCorrectDOMStructure(isTextarea);
|
|
||||||
assertCorrectDestroy(snapshot, setup);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
+151
-38
@@ -7,7 +7,17 @@ import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
|
|||||||
import { generateClassChangeSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
import { generateClassChangeSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
||||||
import { timeout } from '@/testing-browser/timeout';
|
import { timeout } from '@/testing-browser/timeout';
|
||||||
import { OverlayScrollbars } from 'overlayscrollbars';
|
import { OverlayScrollbars } from 'overlayscrollbars';
|
||||||
import { assignDeep, clientSize, from, getBoundingClientRect, style, parent, addClass, WH, removeAttr } from 'support';
|
import {
|
||||||
|
assignDeep,
|
||||||
|
clientSize,
|
||||||
|
from,
|
||||||
|
getBoundingClientRect,
|
||||||
|
style,
|
||||||
|
parent,
|
||||||
|
addClass,
|
||||||
|
WH,
|
||||||
|
removeAttr,
|
||||||
|
} from 'support';
|
||||||
|
|
||||||
interface Metrics {
|
interface Metrics {
|
||||||
offset: {
|
offset: {
|
||||||
@@ -74,8 +84,12 @@ const getMetrics = (elm: HTMLElement): Metrics => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
offset: {
|
offset: {
|
||||||
left: rounding(comparisonBCR.left - comparisonEnvBCR.left).toFixed(Math.min(fixedDigitsOffset, fixedDigits)),
|
left: rounding(comparisonBCR.left - comparisonEnvBCR.left).toFixed(
|
||||||
top: rounding(comparisonBCR.top - comparisonEnvBCR.top).toFixed(Math.min(fixedDigitsOffset, fixedDigits)),
|
Math.min(fixedDigitsOffset, fixedDigits)
|
||||||
|
),
|
||||||
|
top: rounding(comparisonBCR.top - comparisonEnvBCR.top).toFixed(
|
||||||
|
Math.min(fixedDigitsOffset, fixedDigits)
|
||||||
|
),
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
width: rounding(comparisonBCR.width).toFixed(fixedDigits),
|
width: rounding(comparisonBCR.width).toFixed(fixedDigits),
|
||||||
@@ -166,11 +180,11 @@ target!.querySelector('.os-viewport')?.addEventListener('scroll', (e) => {
|
|||||||
resize(target!).addResizeListener((width, height) => {
|
resize(target!).addResizeListener((width, height) => {
|
||||||
style(comparison, { width, height });
|
style(comparison, { width, height });
|
||||||
});
|
});
|
||||||
//resize(comparison!).addResizeListener((width, height) => style(target, { width, height }));
|
// resize(comparison!).addResizeListener((width, height) => style(target, { width, height }));
|
||||||
resize(targetResize!).addResizeListener((width, height) => {
|
resize(targetResize!).addResizeListener((width, height) => {
|
||||||
style(comparisonResize, { width, height });
|
style(comparisonResize, { width, height });
|
||||||
});
|
});
|
||||||
//resize(comparisonRes!).addResizeListener((width, height) => style(targetRes, { width, height }));
|
// resize(comparisonRes!).addResizeListener((width, height) => style(targetRes, { width, height }));
|
||||||
|
|
||||||
const selectCallbackEnv = generateClassChangeSelectCallback(from(envElms));
|
const selectCallbackEnv = generateClassChangeSelectCallback(from(envElms));
|
||||||
const envWidthSelect = document.querySelector<HTMLSelectElement>('#envWidth');
|
const envWidthSelect = document.querySelector<HTMLSelectElement>('#envWidth');
|
||||||
@@ -230,58 +244,136 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
|
|||||||
|
|
||||||
if (isFractionalPixelRatio()) {
|
if (isFractionalPixelRatio()) {
|
||||||
should.ok(
|
should.ok(
|
||||||
plusMinusArr(targetMetrics.scroll.width, fractionalPixelRatioTollerance).indexOf(comparisonMetrics.scroll.width) > -1,
|
plusMinusArr(targetMetrics.scroll.width, fractionalPixelRatioTollerance).indexOf(
|
||||||
|
comparisonMetrics.scroll.width
|
||||||
|
) > -1,
|
||||||
`Scroll width equality. (+-${fractionalPixelRatioTollerance}) | Target: ${targetMetrics.scroll.width} | Comparison: ${comparisonMetrics.scroll.width}`
|
`Scroll width equality. (+-${fractionalPixelRatioTollerance}) | Target: ${targetMetrics.scroll.width} | Comparison: ${comparisonMetrics.scroll.width}`
|
||||||
);
|
);
|
||||||
should.ok(
|
should.ok(
|
||||||
plusMinusArr(targetMetrics.scroll.height, fractionalPixelRatioTollerance).indexOf(comparisonMetrics.scroll.height) > -1,
|
plusMinusArr(targetMetrics.scroll.height, fractionalPixelRatioTollerance).indexOf(
|
||||||
|
comparisonMetrics.scroll.height
|
||||||
|
) > -1,
|
||||||
`Scroll height equality. (+-${fractionalPixelRatioTollerance}) | Target: ${targetMetrics.scroll.height} | Comparison: ${comparisonMetrics.scroll.height}`
|
`Scroll height equality. (+-${fractionalPixelRatioTollerance}) | Target: ${targetMetrics.scroll.height} | Comparison: ${comparisonMetrics.scroll.height}`
|
||||||
);
|
);
|
||||||
|
|
||||||
should.ok(
|
should.ok(
|
||||||
plusMinusArr(osInstance.state()._overflowAmount.w, fractionalPixelRatioTollerance).indexOf(comparisonMetrics.scroll.width) > -1,
|
plusMinusArr(osInstance.state()._overflowAmount.w, fractionalPixelRatioTollerance).indexOf(
|
||||||
`Overflow amount width equality. (+-${fractionalPixelRatioTollerance}) | Amount: ${osInstance.state()._overflowAmount.w} | Comparison: ${
|
|
||||||
comparisonMetrics.scroll.width
|
comparisonMetrics.scroll.width
|
||||||
}`
|
) > -1,
|
||||||
|
`Overflow amount width equality. (+-${fractionalPixelRatioTollerance}) | Amount: ${
|
||||||
|
osInstance.state()._overflowAmount.w
|
||||||
|
} | Comparison: ${comparisonMetrics.scroll.width}`
|
||||||
);
|
);
|
||||||
should.ok(
|
should.ok(
|
||||||
plusMinusArr(osInstance.state()._overflowAmount.h, fractionalPixelRatioTollerance).indexOf(comparisonMetrics.scroll.height) > -1,
|
plusMinusArr(osInstance.state()._overflowAmount.h, fractionalPixelRatioTollerance).indexOf(
|
||||||
`Overflow amount height equality. (+-${fractionalPixelRatioTollerance}) | Amount: ${osInstance.state()._overflowAmount.h} | Comparison: ${
|
|
||||||
comparisonMetrics.scroll.height
|
comparisonMetrics.scroll.height
|
||||||
}`
|
) > -1,
|
||||||
|
`Overflow amount height equality. (+-${fractionalPixelRatioTollerance}) | Amount: ${
|
||||||
|
osInstance.state()._overflowAmount.h
|
||||||
|
} | Comparison: ${comparisonMetrics.scroll.height}`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
should.equal(targetMetrics.scroll.width, comparisonMetrics.scroll.width, 'Scroll width equality.');
|
should.equal(
|
||||||
should.equal(targetMetrics.scroll.height, comparisonMetrics.scroll.height, 'Scroll height equality.');
|
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(
|
||||||
should.equal(osInstance.state()._overflowAmount.h, comparisonMetrics.scroll.height, 'Overflow amount height equality.');
|
osInstance.state()._overflowAmount.w,
|
||||||
|
comparisonMetrics.scroll.width,
|
||||||
|
'Overflow amount width equality.'
|
||||||
|
);
|
||||||
|
should.equal(
|
||||||
|
osInstance.state()._overflowAmount.h,
|
||||||
|
comparisonMetrics.scroll.height,
|
||||||
|
'Overflow amount height equality.'
|
||||||
|
);
|
||||||
|
|
||||||
should.equal(targetMetrics.hasOverflow.x, comparisonMetrics.hasOverflow.x, 'Has overflow x equality.');
|
should.equal(
|
||||||
should.equal(targetMetrics.hasOverflow.y, comparisonMetrics.hasOverflow.y, 'Has overflow y equality.');
|
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) {
|
if (targetMetrics.hasOverflow.x) {
|
||||||
should.equal(style(targetViewport!, 'overflowX'), 'scroll', 'Overflow-X should result in scroll.');
|
should.equal(
|
||||||
should.ok(osInstance.state()._overflowAmount.w > 0, 'Overflow amount width should be > 0 with overflow.');
|
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.'
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
should.notEqual(style(targetViewport!, 'overflowX'), 'scroll', 'No Overflow-X shouldnt result in scroll.');
|
should.notEqual(
|
||||||
should.equal(osInstance.state()._overflowAmount.w, 0, 'Overflow amount width should be 0 without overflow.');
|
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.hasOverflow.y) {
|
if (targetMetrics.hasOverflow.y) {
|
||||||
should.equal(style(targetViewport!, 'overflowY'), 'scroll', 'Overflow-Y should result in scroll.');
|
should.equal(
|
||||||
should.ok(osInstance.state()._overflowAmount.h > 0, 'Overflow amount height should be > 0 with overflow.');
|
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.'
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
should.notEqual(style(targetViewport!, 'overflowY'), 'scroll', 'No Overflow-Y shouldnt result in scroll.');
|
should.notEqual(
|
||||||
should.equal(osInstance.state()._overflowAmount.h, 0, 'Overflow amount height should be 0 without overflow.');
|
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.');
|
should.equal(
|
||||||
should.equal(targetMetrics.percentElm.height, comparisonMetrics.percentElm.height, 'Percent Elements height equality.');
|
targetMetrics.percentElm.width,
|
||||||
|
comparisonMetrics.percentElm.width,
|
||||||
|
'Percent Elements width equality.'
|
||||||
|
);
|
||||||
|
should.equal(
|
||||||
|
targetMetrics.percentElm.height,
|
||||||
|
comparisonMetrics.percentElm.height,
|
||||||
|
'Percent Elements height equality.'
|
||||||
|
);
|
||||||
|
|
||||||
should.equal(targetMetrics.endElm.width, comparisonMetrics.endElm.width, 'End Elements width equality.');
|
should.equal(
|
||||||
should.equal(targetMetrics.endElm.height, comparisonMetrics.endElm.height, 'End Elements height equality.');
|
targetMetrics.endElm.width,
|
||||||
|
comparisonMetrics.endElm.width,
|
||||||
|
'End Elements width equality.'
|
||||||
|
);
|
||||||
|
should.equal(
|
||||||
|
targetMetrics.endElm.height,
|
||||||
|
comparisonMetrics.endElm.height,
|
||||||
|
'End Elements height equality.'
|
||||||
|
);
|
||||||
|
|
||||||
await timeout(1);
|
await timeout(1);
|
||||||
|
|
||||||
@@ -307,31 +399,36 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
|
|||||||
afterEach,
|
afterEach,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
/*
|
||||||
const iterateEnvWidth = async (afterEach?: () => any) => {
|
const iterateEnvWidth = async (afterEach?: () => any) => {
|
||||||
await iterate(envWidthSelect, afterEach);
|
await iterate(envWidthSelect, afterEach);
|
||||||
};
|
};
|
||||||
const iterateEnvHeight = async (afterEach?: () => any) => {
|
const iterateEnvHeight = async (afterEach?: () => any) => {
|
||||||
await iterate(envHeightSelect, afterEach);
|
await iterate(envHeightSelect, afterEach);
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
const iterateHeight = async (afterEach?: () => any) => {
|
const iterateHeight = async (afterEach?: () => any) => {
|
||||||
await iterate(containerHeightSelect, afterEach);
|
await iterate(containerHeightSelect, afterEach);
|
||||||
};
|
};
|
||||||
const iterateWidth = async (afterEach?: () => any) => {
|
const iterateWidth = async (afterEach?: () => any) => {
|
||||||
await iterate(containerWidthSelect, afterEach);
|
await iterate(containerWidthSelect, afterEach);
|
||||||
};
|
};
|
||||||
|
/*
|
||||||
const iterateFloat = async (afterEach?: () => any) => {
|
const iterateFloat = async (afterEach?: () => any) => {
|
||||||
await iterate(containerFloatSelect, afterEach);
|
await iterate(containerFloatSelect, afterEach);
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
const iteratePadding = async (afterEach?: () => any) => {
|
const iteratePadding = async (afterEach?: () => any) => {
|
||||||
await iterate(containerPaddingSelect, afterEach);
|
await iterate(containerPaddingSelect, afterEach);
|
||||||
};
|
};
|
||||||
const iterateBorder = async (afterEach?: () => any) => {
|
const iterateBorder = async (afterEach?: () => any) => {
|
||||||
await iterate(containerBorderSelect, afterEach);
|
await iterate(containerBorderSelect, afterEach);
|
||||||
};
|
};
|
||||||
|
/*
|
||||||
const iterateMargin = async (afterEach?: () => any) => {
|
const iterateMargin = async (afterEach?: () => any) => {
|
||||||
await iterate(containerMarginSelect, afterEach);
|
await iterate(containerMarginSelect, afterEach);
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
const iterateBoxSizing = async (afterEach?: () => any) => {
|
const iterateBoxSizing = async (afterEach?: () => any) => {
|
||||||
await iterate(containerBoxSizingSelect, afterEach);
|
await iterate(containerBoxSizingSelect, afterEach);
|
||||||
};
|
};
|
||||||
@@ -354,8 +451,10 @@ const overflowTest = async () => {
|
|||||||
const computedStyle = window.getComputedStyle(elm);
|
const computedStyle = window.getComputedStyle(elm);
|
||||||
const size = clientSize(elm);
|
const size = clientSize(elm);
|
||||||
return {
|
return {
|
||||||
w: size.w - (parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)),
|
w:
|
||||||
h: size.h - (parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)),
|
size.w - (parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)),
|
||||||
|
h:
|
||||||
|
size.h - (parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,15 +524,29 @@ const overflowTest = async () => {
|
|||||||
|
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
if (width) {
|
if (width) {
|
||||||
should.ok(overflowAmountCheck.width >= addOverflow, 'Correct smallest possible overflow width. (?)');
|
should.ok(
|
||||||
|
overflowAmountCheck.width >= addOverflow,
|
||||||
|
'Correct smallest possible overflow width. (?)'
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
should.equal(overflowAmountCheck.width, 0, 'Correct smallest possible overflow width. (0)');
|
should.equal(
|
||||||
|
overflowAmountCheck.width,
|
||||||
|
0,
|
||||||
|
'Correct smallest possible overflow width. (0)'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height) {
|
if (height) {
|
||||||
should.ok(overflowAmountCheck.height >= addOverflow, 'Correct smallest possible overflow height. (?)');
|
should.ok(
|
||||||
|
overflowAmountCheck.height >= addOverflow,
|
||||||
|
'Correct smallest possible overflow height. (?)'
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
should.equal(overflowAmountCheck.height, 0, 'Correct smallest possible overflow height. (0)');
|
should.equal(
|
||||||
|
overflowAmountCheck.height,
|
||||||
|
0,
|
||||||
|
'Correct smallest possible overflow height. (0)'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
|
||||||
|
import { test, Page } from '@playwright/test';
|
||||||
|
|
||||||
|
playwrightRollup();
|
||||||
|
|
||||||
|
test.describe('StructureLifecycle', () => {
|
||||||
|
[false, true].forEach(async (nativeScrollbarStyling) => {
|
||||||
|
const withText = nativeScrollbarStyling ? 'with' : 'without';
|
||||||
|
const nss = async (page: Page) => {
|
||||||
|
if (!nativeScrollbarStyling) {
|
||||||
|
await page.click('#nss');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test.describe(`structureLifecycles ${withText} native scrollbar styling`, () => {
|
||||||
|
test.describe.configure({ mode: 'parallel' });
|
||||||
|
|
||||||
|
test('default', async ({ page }) => {
|
||||||
|
await nss(page);
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('without flexbox glue & css custom props', async ({ page }) => {
|
||||||
|
await nss(page);
|
||||||
|
await page.click('#fbg');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.click('#ccp');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with partially overlaid scrollbars', async ({ page, browserName }) => {
|
||||||
|
test.skip(
|
||||||
|
browserName === 'firefox' || browserName === 'webkit',
|
||||||
|
"firefox can't simulate partially overlaid scrollbars, boost speed by omitting webkit"
|
||||||
|
);
|
||||||
|
|
||||||
|
await nss(page);
|
||||||
|
await page.click('#po');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with fully overlaid scrollbars', async ({ page }) => {
|
||||||
|
await nss(page);
|
||||||
|
await page.click('#fo');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
+144
-36
@@ -4,7 +4,19 @@ import should from 'should';
|
|||||||
import { generateSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
import { generateSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
||||||
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 { appendChildren, createDiv, removeElements, children, isArray, isNumber, liesBetween, addClass, removeClass, diffClass, on } from 'support';
|
import {
|
||||||
|
appendChildren,
|
||||||
|
createDiv,
|
||||||
|
removeElements,
|
||||||
|
children,
|
||||||
|
isArray,
|
||||||
|
isNumber,
|
||||||
|
liesBetween,
|
||||||
|
addClass,
|
||||||
|
removeClass,
|
||||||
|
diffClass,
|
||||||
|
on,
|
||||||
|
} from 'support';
|
||||||
|
|
||||||
import { createDOMObserver } from 'observers/domObserver';
|
import { createDOMObserver } from 'observers/domObserver';
|
||||||
|
|
||||||
@@ -27,8 +39,12 @@ const targetElm: HTMLElement | null = document.querySelector('#target');
|
|||||||
const trargetContentElm: HTMLElement | null = document.querySelector('#target .content');
|
const trargetContentElm: HTMLElement | null = document.querySelector('#target .content');
|
||||||
const targetElmContentElm: HTMLElement | null = document.querySelector('#content-host');
|
const targetElmContentElm: HTMLElement | null = document.querySelector('#content-host');
|
||||||
const contentElmAttrChange: HTMLElement | null = document.querySelector('#target .content-nest');
|
const contentElmAttrChange: HTMLElement | null = document.querySelector('#target .content-nest');
|
||||||
const contentBetweenElmAttrChange: HTMLElement | null = document.querySelector('#content-host .padding-nest-item');
|
const contentBetweenElmAttrChange: HTMLElement | null = document.querySelector(
|
||||||
const contentHostElmAttrChange: HTMLElement | null = document.querySelector('#content-nest-item-host');
|
'#content-host .padding-nest-item'
|
||||||
|
);
|
||||||
|
const contentHostElmAttrChange: HTMLElement | null = document.querySelector(
|
||||||
|
'#content-nest-item-host'
|
||||||
|
);
|
||||||
|
|
||||||
const targetElmsSlot = document.querySelector('#target .host-nest-item');
|
const targetElmsSlot = document.querySelector('#target .host-nest-item');
|
||||||
const targetContentElmsSlot = document.querySelector('#target .content .content-nest');
|
const targetContentElmsSlot = document.querySelector('#target .content .content-nest');
|
||||||
@@ -36,20 +52,40 @@ const targetContentBetweenElmsSlot = document.querySelector('#content-host');
|
|||||||
const imgElmsSlot = document.querySelector('#target .content-nest');
|
const imgElmsSlot = document.querySelector('#target .content-nest');
|
||||||
const transitionElmsSlot = document.querySelector('#content-host .content');
|
const transitionElmsSlot = document.querySelector('#content-host .content');
|
||||||
|
|
||||||
const addRemoveTargetElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetElms');
|
const addRemoveTargetElms: HTMLButtonElement | null = document.querySelector(
|
||||||
const addRemoveTargetContentElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetContentElms');
|
'#addRemoveTargetElms'
|
||||||
const addRemoveTargetContentBetweenElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetContentBetweenElms');
|
);
|
||||||
|
const addRemoveTargetContentElms: HTMLButtonElement | null = document.querySelector(
|
||||||
|
'#addRemoveTargetContentElms'
|
||||||
|
);
|
||||||
|
const addRemoveTargetContentBetweenElms: HTMLButtonElement | null = document.querySelector(
|
||||||
|
'#addRemoveTargetContentBetweenElms'
|
||||||
|
);
|
||||||
const addRemoveImgElms: HTMLButtonElement | null = document.querySelector('#addRemoveImgElms');
|
const addRemoveImgElms: HTMLButtonElement | null = document.querySelector('#addRemoveImgElms');
|
||||||
const addRemoveTransitionElms: HTMLButtonElement | null = document.querySelector('#addRemoveTransitionElms');
|
const addRemoveTransitionElms: HTMLButtonElement | null = document.querySelector(
|
||||||
|
'#addRemoveTransitionElms'
|
||||||
|
);
|
||||||
const ignoreTargetChange: HTMLButtonElement | null = document.querySelector('#ignoreTargetChange');
|
const ignoreTargetChange: HTMLButtonElement | null = document.querySelector('#ignoreTargetChange');
|
||||||
const setTargetAttr: HTMLSelectElement | null = document.querySelector('#setTargetAttr');
|
const setTargetAttr: HTMLSelectElement | null = document.querySelector('#setTargetAttr');
|
||||||
const setFilteredTargetAttr: HTMLSelectElement | null = document.querySelector('#setFilteredTargetAttr');
|
const setFilteredTargetAttr: HTMLSelectElement | null = document.querySelector(
|
||||||
|
'#setFilteredTargetAttr'
|
||||||
|
);
|
||||||
const setContentAttr: HTMLSelectElement | null = document.querySelector('#setContentAttr');
|
const setContentAttr: HTMLSelectElement | null = document.querySelector('#setContentAttr');
|
||||||
const setFilteredContentAttr: HTMLSelectElement | null = document.querySelector('#setFilteredContentAttr');
|
const setFilteredContentAttr: HTMLSelectElement | null = document.querySelector(
|
||||||
const setContentBetweenAttr: HTMLSelectElement | null = document.querySelector('#setContentBetweenAttr');
|
'#setFilteredContentAttr'
|
||||||
const setFilteredContentBetweenAttr: HTMLSelectElement | null = document.querySelector('#setFilteredContentBetweenAttr');
|
);
|
||||||
const setContentHostElmAttr: HTMLSelectElement | null = document.querySelector('#setContentHostElmAttr');
|
const setContentBetweenAttr: HTMLSelectElement | null = document.querySelector(
|
||||||
const setFilteredContentHostElmAttr: HTMLSelectElement | null = document.querySelector('#setFilteredContentHostElmAttr');
|
'#setContentBetweenAttr'
|
||||||
|
);
|
||||||
|
const setFilteredContentBetweenAttr: HTMLSelectElement | null = document.querySelector(
|
||||||
|
'#setFilteredContentBetweenAttr'
|
||||||
|
);
|
||||||
|
const setContentHostElmAttr: HTMLSelectElement | null = document.querySelector(
|
||||||
|
'#setContentHostElmAttr'
|
||||||
|
);
|
||||||
|
const setFilteredContentHostElmAttr: HTMLSelectElement | null = document.querySelector(
|
||||||
|
'#setFilteredContentHostElmAttr'
|
||||||
|
);
|
||||||
const summaryContent: HTMLElement | null = document.querySelector('#summary-content');
|
const summaryContent: HTMLElement | null = document.querySelector('#summary-content');
|
||||||
const summaryBetween: HTMLElement | null = document.querySelector('#summary-between');
|
const summaryBetween: HTMLElement | null = document.querySelector('#summary-between');
|
||||||
|
|
||||||
@@ -66,11 +102,21 @@ const targetDomObserver = createDOMObserver(
|
|||||||
document.querySelector('#target')!,
|
document.querySelector('#target')!,
|
||||||
false,
|
false,
|
||||||
(changedTargetAttrs: string[], styleChanged: boolean) => {
|
(changedTargetAttrs: string[], styleChanged: boolean) => {
|
||||||
should.ok(Array.isArray(changedTargetAttrs), 'The changedTargetAttrs parameter in a target dom observer must be a array.');
|
should.ok(
|
||||||
should.equal(typeof styleChanged, 'boolean', 'The styleChanged parameter in a target dom observer must be a boolean.');
|
Array.isArray(changedTargetAttrs),
|
||||||
|
'The changedTargetAttrs parameter in a target dom observer must be a array.'
|
||||||
|
);
|
||||||
|
should.equal(
|
||||||
|
typeof styleChanged,
|
||||||
|
'boolean',
|
||||||
|
'The styleChanged parameter in a target dom observer must be a boolean.'
|
||||||
|
);
|
||||||
|
|
||||||
if (styleChanged && changedTargetAttrs.length === 0) {
|
if (styleChanged && changedTargetAttrs.length === 0) {
|
||||||
should.ok(false, 'Style changing properties must always be inside the changedTargetAttrs array.');
|
should.ok(
|
||||||
|
false,
|
||||||
|
'Style changing properties must always be inside the changedTargetAttrs array.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
domTargetObserverObservations.push({ changedTargetAttrs, styleChanged });
|
domTargetObserverObservations.push({ changedTargetAttrs, styleChanged });
|
||||||
@@ -106,14 +152,23 @@ const targetDomObserver = createDOMObserver(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const createContentDomOserver = (eventContentChange: Array<[string?, string?] | null | undefined>) => {
|
const createContentDomOserver = (
|
||||||
|
eventContentChange: Array<[string?, string?] | null | undefined>
|
||||||
|
) => {
|
||||||
return createDOMObserver(
|
return createDOMObserver(
|
||||||
trargetContentElm!,
|
trargetContentElm!,
|
||||||
true,
|
true,
|
||||||
(contentChangedTroughEvent: boolean) => {
|
(contentChangedTroughEvent: boolean) => {
|
||||||
should.equal(typeof contentChangedTroughEvent, 'boolean', 'The contentChanged parameter in a content dom observer must be a boolean.');
|
should.equal(
|
||||||
|
typeof contentChangedTroughEvent,
|
||||||
|
'boolean',
|
||||||
|
'The contentChanged parameter in a content dom observer must be a boolean.'
|
||||||
|
);
|
||||||
|
|
||||||
domContentObserverObservations.push({ contentChange: true, troughEvent: contentChangedTroughEvent });
|
domContentObserverObservations.push({
|
||||||
|
contentChange: true,
|
||||||
|
troughEvent: contentChangedTroughEvent,
|
||||||
|
});
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (contentChangesCountSlot) {
|
if (contentChangesCountSlot) {
|
||||||
contentChangesCountSlot.textContent = `${domContentObserverObservations.length}`;
|
contentChangesCountSlot.textContent = `${domContentObserverObservations.length}`;
|
||||||
@@ -127,7 +182,11 @@ const createContentDomOserver = (eventContentChange: Array<[string?, string?] |
|
|||||||
_nestedTargetSelector: hostSelector,
|
_nestedTargetSelector: hostSelector,
|
||||||
_ignoreContentChange: (mutation, isNestedTarget) => {
|
_ignoreContentChange: (mutation, isNestedTarget) => {
|
||||||
const { target, attributeName } = mutation;
|
const { target, attributeName } = mutation;
|
||||||
return isNestedTarget ? false : attributeName ? liesBetween(target as Element, hostSelector, '.content') : false;
|
return isNestedTarget
|
||||||
|
? false
|
||||||
|
: attributeName
|
||||||
|
? liesBetween(target as Element, hostSelector, '.content')
|
||||||
|
: false;
|
||||||
},
|
},
|
||||||
_ignoreNestedTargetChange: (target, attrName, oldValue, newValue) => {
|
_ignoreNestedTargetChange: (target, attrName, oldValue, newValue) => {
|
||||||
if (attrName === 'class' && oldValue && newValue) {
|
if (attrName === 'class' && oldValue && newValue) {
|
||||||
@@ -149,8 +208,10 @@ const createContentDomOserver = (eventContentChange: Array<[string?, string?] |
|
|||||||
|
|
||||||
let contentDomObserver = createContentDomOserver(contentChange);
|
let contentDomObserver = createContentDomOserver(contentChange);
|
||||||
|
|
||||||
const getTotalObservations = () => domTargetObserverObservations.length + domContentObserverObservations.length;
|
const getTotalObservations = () =>
|
||||||
const getLast = <T>(arr: T[], indexFromLast = 0): T => arr[arr.length - 1 - indexFromLast] || ({} as T);
|
domTargetObserverObservations.length + domContentObserverObservations.length;
|
||||||
|
const getLast = <T>(arr: T[], indexFromLast = 0): T =>
|
||||||
|
arr[arr.length - 1 - indexFromLast] || ({} as T);
|
||||||
const changedThrough = <ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult>(
|
const changedThrough = <ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult>(
|
||||||
observationLists?: Array<ChangeThrough[]> | ChangeThrough[]
|
observationLists?: Array<ChangeThrough[]> | ChangeThrough[]
|
||||||
) => {
|
) => {
|
||||||
@@ -222,7 +283,9 @@ const attrChangeListener = (attrChangeTarget: HTMLElement | null) =>
|
|||||||
isClass && target.classList.add('something');
|
isClass && target.classList.add('something');
|
||||||
!isClass && target.setAttribute(selectedValue, 'something');
|
!isClass && target.setAttribute(selectedValue, 'something');
|
||||||
});
|
});
|
||||||
const iterateAttrChange = async <ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult>(
|
const iterateAttrChange = async <
|
||||||
|
ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult
|
||||||
|
>(
|
||||||
select: HTMLSelectElement | null,
|
select: HTMLSelectElement | null,
|
||||||
changeThrough?: ChangeThrough[],
|
changeThrough?: ChangeThrough[],
|
||||||
checkChange?: (observation: ChangeThrough, selected: string) => any
|
checkChange?: (observation: ChangeThrough, selected: string) => any
|
||||||
@@ -248,10 +311,17 @@ const iterateAttrChange = async <ChangeThrough extends DOMContentObserverResult
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMContentObserverResult[] | SeparateChangeThrough) => {
|
const addRemoveElementsTest = async (
|
||||||
|
slot: Element | null,
|
||||||
|
changeThrough?: DOMContentObserverResult[] | SeparateChangeThrough
|
||||||
|
) => {
|
||||||
if (slot) {
|
if (slot) {
|
||||||
let addChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as DOMContentObserverResult[] | undefined;
|
let addChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as
|
||||||
let removeChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as DOMContentObserverResult[] | undefined;
|
| DOMContentObserverResult[]
|
||||||
|
| undefined;
|
||||||
|
let removeChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as
|
||||||
|
| DOMContentObserverResult[]
|
||||||
|
| undefined;
|
||||||
if (changeThrough && !isArray(changeThrough)) {
|
if (changeThrough && !isArray(changeThrough)) {
|
||||||
addChangeThrough = (changeThrough as SeparateChangeThrough).added;
|
addChangeThrough = (changeThrough as SeparateChangeThrough).added;
|
||||||
removeChangeThrough = (changeThrough as SeparateChangeThrough).removed;
|
removeChangeThrough = (changeThrough as SeparateChangeThrough).removed;
|
||||||
@@ -272,7 +342,11 @@ const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMCo
|
|||||||
if (addChangeThrough) {
|
if (addChangeThrough) {
|
||||||
const contentChanged = getLast(addChangeThrough);
|
const contentChanged = getLast(addChangeThrough);
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
should.deepEqual(contentChanged, { contentChange: true, troughEvent: false }, 'Adding an content element must result in a content change.');
|
should.deepEqual(
|
||||||
|
contentChanged,
|
||||||
|
{ contentChange: true, troughEvent: false },
|
||||||
|
'Adding an content element must result in a content change.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -311,7 +385,10 @@ const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMCo
|
|||||||
await removeElm();
|
await removeElm();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const triggerSummaryElemet = async (summaryElm: HTMLElement | null, changeThrough?: DOMContentObserverResult[]) => {
|
const triggerSummaryElemet = async (
|
||||||
|
summaryElm: HTMLElement | null,
|
||||||
|
changeThrough?: DOMContentObserverResult[]
|
||||||
|
) => {
|
||||||
// onyl do if summary is working (IE. exception)
|
// onyl do if summary is working (IE. exception)
|
||||||
if (summaryElm && (summaryElm.nextElementSibling as HTMLElement)?.offsetHeight === 0) {
|
if (summaryElm && (summaryElm.nextElementSibling as HTMLElement)?.offsetHeight === 0) {
|
||||||
const click = async () => {
|
const click = async () => {
|
||||||
@@ -370,7 +447,11 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const lastContentChanged = getLast(domContentObserverObservations);
|
const lastContentChanged = getLast(domContentObserverObservations);
|
||||||
should.deepEqual(lastContentChanged, { contentChange: true, troughEvent: true }, 'The images load event must result in a content change.');
|
should.deepEqual(
|
||||||
|
lastContentChanged,
|
||||||
|
{ contentChange: true, troughEvent: true },
|
||||||
|
'The images load event must result in a content change.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -424,7 +505,9 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
await addMultiple();
|
await addMultiple();
|
||||||
|
|
||||||
// remove load event from image test
|
// remove load event from image test
|
||||||
const addChanged = async (newEventContentChange: Array<[string?, string?] | null | undefined>) => {
|
const addChanged = async (
|
||||||
|
newEventContentChange: Array<[string?, string?] | null | undefined>
|
||||||
|
) => {
|
||||||
contentDomObserver._destroy();
|
contentDomObserver._destroy();
|
||||||
contentDomObserver = createContentDomOserver(newEventContentChange);
|
contentDomObserver = createContentDomOserver(newEventContentChange);
|
||||||
|
|
||||||
@@ -449,7 +532,16 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
contentDomObserver = createContentDomOserver(contentChange);
|
contentDomObserver = createContentDomOserver(contentChange);
|
||||||
};
|
};
|
||||||
|
|
||||||
await addChanged([['img', 'something'], ['img', 'something2'], ['img', ''], ['img', undefined], ['', ''], [undefined, undefined], null, undefined]);
|
await addChanged([
|
||||||
|
['img', 'something'],
|
||||||
|
['img', 'something2'],
|
||||||
|
['img', ''],
|
||||||
|
['img', undefined],
|
||||||
|
['', ''],
|
||||||
|
[undefined, undefined],
|
||||||
|
null,
|
||||||
|
undefined,
|
||||||
|
]);
|
||||||
await addChanged([]);
|
await addChanged([]);
|
||||||
|
|
||||||
removeElements(document.querySelectorAll('.img'));
|
removeElements(document.querySelectorAll('.img'));
|
||||||
@@ -460,7 +552,11 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
const startTransition = async (elm: Element, expectTransitionEndContentChange: boolean) => {
|
const startTransition = async (elm: Element, expectTransitionEndContentChange: boolean) => {
|
||||||
await timeout(50); // time for css to apply class a bit later to trigger transition
|
await timeout(50); // time for css to apply class a bit later to trigger transition
|
||||||
|
|
||||||
const { before: beforeTransition, after: afterTransition, compare: compareTransition } = changedThrough(domContentObserverObservations);
|
const {
|
||||||
|
before: beforeTransition,
|
||||||
|
after: afterTransition,
|
||||||
|
compare: compareTransition,
|
||||||
|
} = changedThrough(domContentObserverObservations);
|
||||||
beforeTransition();
|
beforeTransition();
|
||||||
removeClass(elm, 'resetTransition'); // IE...
|
removeClass(elm, 'resetTransition'); // IE...
|
||||||
addClass(elm, 'active');
|
addClass(elm, 'active');
|
||||||
@@ -523,7 +619,9 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
await add(false);
|
await add(false);
|
||||||
|
|
||||||
contentDomObserver._destroy();
|
contentDomObserver._destroy();
|
||||||
contentDomObserver = createContentDomOserver(contentChange.concat([['.transition', 'transitionend']]));
|
contentDomObserver = createContentDomOserver(
|
||||||
|
contentChange.concat([['.transition', 'transitionend']])
|
||||||
|
);
|
||||||
|
|
||||||
await add(true);
|
await add(true);
|
||||||
};
|
};
|
||||||
@@ -557,7 +655,11 @@ const iterateTargetAttrChange = async () => {
|
|||||||
true,
|
true,
|
||||||
'A attribute change on the target element for a DOMTargetObserver must be inside the changedTargetAttrs array.'
|
'A attribute change on the target element for a DOMTargetObserver must be inside the changedTargetAttrs array.'
|
||||||
);
|
);
|
||||||
should.equal(styleChanged, true, 'A style changing attribute on the target element for a DOMTargetObserver must set styleChanged to true.');
|
should.equal(
|
||||||
|
styleChanged,
|
||||||
|
true,
|
||||||
|
'A style changing attribute on the target element for a DOMTargetObserver must set styleChanged to true.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
await iterateAttrChange(setFilteredTargetAttr);
|
await iterateAttrChange(setFilteredTargetAttr);
|
||||||
};
|
};
|
||||||
@@ -605,9 +707,15 @@ setFilteredTargetAttr?.addEventListener('change', attrChangeListener(targetElm))
|
|||||||
setContentAttr?.addEventListener('change', attrChangeListener(contentElmAttrChange));
|
setContentAttr?.addEventListener('change', attrChangeListener(contentElmAttrChange));
|
||||||
setFilteredContentAttr?.addEventListener('change', attrChangeListener(contentElmAttrChange));
|
setFilteredContentAttr?.addEventListener('change', attrChangeListener(contentElmAttrChange));
|
||||||
setContentBetweenAttr?.addEventListener('change', attrChangeListener(contentBetweenElmAttrChange));
|
setContentBetweenAttr?.addEventListener('change', attrChangeListener(contentBetweenElmAttrChange));
|
||||||
setFilteredContentBetweenAttr?.addEventListener('change', attrChangeListener(contentBetweenElmAttrChange));
|
setFilteredContentBetweenAttr?.addEventListener(
|
||||||
|
'change',
|
||||||
|
attrChangeListener(contentBetweenElmAttrChange)
|
||||||
|
);
|
||||||
setContentHostElmAttr?.addEventListener('change', attrChangeListener(contentHostElmAttrChange));
|
setContentHostElmAttr?.addEventListener('change', attrChangeListener(contentHostElmAttrChange));
|
||||||
setFilteredContentHostElmAttr?.addEventListener('change', attrChangeListener(contentHostElmAttrChange));
|
setFilteredContentHostElmAttr?.addEventListener(
|
||||||
|
'change',
|
||||||
|
attrChangeListener(contentHostElmAttrChange)
|
||||||
|
);
|
||||||
|
|
||||||
const start = async () => {
|
const start = async () => {
|
||||||
setTestResult(null);
|
setTestResult(null);
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
|
||||||
|
import { test } from '@playwright/test';
|
||||||
|
|
||||||
|
playwrightRollup();
|
||||||
|
|
||||||
|
test.describe('DOMObserver', () => {
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
});
|
||||||
+50
-12
@@ -2,7 +2,11 @@ import 'styles/overlayscrollbars.scss';
|
|||||||
import './index.scss';
|
import './index.scss';
|
||||||
import './handleEnvironment';
|
import './handleEnvironment';
|
||||||
import should from 'should';
|
import should from 'should';
|
||||||
import { generateClassChangeSelectCallback, iterateSelect, selectOption } from '@/testing-browser/Select';
|
import {
|
||||||
|
generateClassChangeSelectCallback,
|
||||||
|
iterateSelect,
|
||||||
|
selectOption,
|
||||||
|
} from '@/testing-browser/Select';
|
||||||
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
|
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
|
||||||
import { timeout } from '@/testing-browser/timeout';
|
import { timeout } from '@/testing-browser/timeout';
|
||||||
import { hasDimensions, offsetSize, WH, style } from 'support';
|
import { hasDimensions, offsetSize, WH, style } from 'support';
|
||||||
@@ -15,8 +19,12 @@ const contentBox = (elm: HTMLElement | null): WH<number> => {
|
|||||||
if (elm) {
|
if (elm) {
|
||||||
const computedStyle = window.getComputedStyle(elm);
|
const computedStyle = window.getComputedStyle(elm);
|
||||||
return {
|
return {
|
||||||
w: elm.clientWidth - (parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)),
|
w:
|
||||||
h: elm.clientHeight - (parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)),
|
elm.clientWidth -
|
||||||
|
(parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)),
|
||||||
|
h:
|
||||||
|
elm.clientHeight -
|
||||||
|
(parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +45,7 @@ const preInitChildren = targetElm?.children.length;
|
|||||||
|
|
||||||
const sizeObserver = createSizeObserver(
|
const sizeObserver = createSizeObserver(
|
||||||
targetElm as HTMLElement,
|
targetElm as HTMLElement,
|
||||||
({ _directionIsRTLCache, _sizeChanged, _appear }) => {
|
({ _directionIsRTLCache, _sizeChanged }) => {
|
||||||
if (_sizeChanged) {
|
if (_sizeChanged) {
|
||||||
sizeIterations += 1;
|
sizeIterations += 1;
|
||||||
}
|
}
|
||||||
@@ -45,6 +53,7 @@ const sizeObserver = createSizeObserver(
|
|||||||
if (_directionIsRTLCache) {
|
if (_directionIsRTLCache) {
|
||||||
directionIterations += 1;
|
directionIterations += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (resizesSlot) {
|
if (resizesSlot) {
|
||||||
resizesSlot.textContent = (directionIterations + sizeIterations).toString();
|
resizesSlot.textContent = (directionIterations + sizeIterations).toString();
|
||||||
@@ -83,13 +92,22 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
|
|||||||
currBoxSizing,
|
currBoxSizing,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async check({ currSizeIterations, currDirectionIterations, currOffsetSize, currContentSize, currDir, currBoxSizing }) {
|
async check({
|
||||||
|
currSizeIterations,
|
||||||
|
currDirectionIterations,
|
||||||
|
currOffsetSize,
|
||||||
|
currContentSize,
|
||||||
|
currDir,
|
||||||
|
currBoxSizing,
|
||||||
|
}) {
|
||||||
const newOffsetSize = offsetSize(targetElm as HTMLElement);
|
const newOffsetSize = offsetSize(targetElm as HTMLElement);
|
||||||
const newContentSize = contentBox(targetElm as HTMLElement);
|
const newContentSize = contentBox(targetElm as HTMLElement);
|
||||||
const newDir = style(targetElm as HTMLElement, 'direction');
|
const newDir = style(targetElm as HTMLElement, 'direction');
|
||||||
const newBoxSizing = style(targetElm as HTMLElement, 'box-sizing');
|
const newBoxSizing = style(targetElm as HTMLElement, 'box-sizing');
|
||||||
const offsetSizeChanged = currOffsetSize.w !== newOffsetSize.w || currOffsetSize.h !== newOffsetSize.h;
|
const offsetSizeChanged =
|
||||||
const contentSizeChanged = currContentSize.w !== newContentSize.w || currContentSize.h !== newContentSize.h;
|
currOffsetSize.w !== newOffsetSize.w || currOffsetSize.h !== newOffsetSize.h;
|
||||||
|
const contentSizeChanged =
|
||||||
|
currContentSize.w !== newContentSize.w || currContentSize.h !== newContentSize.h;
|
||||||
const dirChanged = currDir !== newDir;
|
const dirChanged = currDir !== newDir;
|
||||||
const boxSizingChanged = currBoxSizing !== newBoxSizing;
|
const boxSizingChanged = currBoxSizing !== newBoxSizing;
|
||||||
const dimensions = hasDimensions(targetElm as HTMLElement);
|
const dimensions = hasDimensions(targetElm as HTMLElement);
|
||||||
@@ -113,20 +131,36 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
|
|||||||
if (dirChanged) {
|
if (dirChanged) {
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
const expectedCacheValue = newDir === 'rtl';
|
const expectedCacheValue = newDir === 'rtl';
|
||||||
should.equal(directionIterations, currDirectionIterations + 1, 'Direction change was detected correctly.');
|
should.equal(
|
||||||
should.equal(sizeObserver._getCurrentCacheValues()._directionIsRTL._value, expectedCacheValue, 'Direction cache value is correct.');
|
directionIterations,
|
||||||
|
currDirectionIterations + 1,
|
||||||
|
'Direction change was detected correctly.'
|
||||||
|
);
|
||||||
|
should.equal(
|
||||||
|
sizeObserver._getCurrentCacheValues()._directionIsRTL[0],
|
||||||
|
expectedCacheValue,
|
||||||
|
'Direction cache value is correct.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (boxSizingChanged) {
|
if (boxSizingChanged) {
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
should.equal(sizeIterations, currSizeIterations + 1, 'BoxSizing change was detected correctly.');
|
should.equal(
|
||||||
|
sizeIterations,
|
||||||
|
currSizeIterations + 1,
|
||||||
|
'BoxSizing change was detected correctly.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dimensions && (offsetSizeChanged || contentSizeChanged)) {
|
if (dimensions && (offsetSizeChanged || contentSizeChanged)) {
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
should.equal(sizeIterations, currSizeIterations + 1, 'Size change was detected correctly.');
|
should.equal(
|
||||||
|
sizeIterations,
|
||||||
|
currSizeIterations + 1,
|
||||||
|
'Size change was detected correctly.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +263,11 @@ const start = async () => {
|
|||||||
await cleanBoxSizingChange();
|
await cleanBoxSizingChange();
|
||||||
|
|
||||||
sizeObserver._destroy();
|
sizeObserver._destroy();
|
||||||
should.equal(targetElm?.children.length, preInitChildren, 'Destruction removes all generated elements.');
|
should.equal(
|
||||||
|
targetElm?.children.length,
|
||||||
|
preInitChildren,
|
||||||
|
'Destruction removes all generated elements.'
|
||||||
|
);
|
||||||
setTestResult(true);
|
setTestResult(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
|
||||||
|
import { test } from '@playwright/test';
|
||||||
|
|
||||||
|
playwrightRollup();
|
||||||
|
|
||||||
|
test.describe('SizeObserver', () => {
|
||||||
|
test('with ResizeOserver', async ({ page }) => {
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with ResizeOserver polyfill', async ({ page }) => {
|
||||||
|
await page.click('#roPolyfill');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
});
|
||||||
+29
-8
@@ -2,7 +2,11 @@ import 'styles/overlayscrollbars.scss';
|
|||||||
import './index.scss';
|
import './index.scss';
|
||||||
import './handleEnvironment';
|
import './handleEnvironment';
|
||||||
import should from 'should';
|
import should from 'should';
|
||||||
import { generateClassChangeSelectCallback, iterateSelect, selectOption } from '@/testing-browser/Select';
|
import {
|
||||||
|
generateClassChangeSelectCallback,
|
||||||
|
iterateSelect,
|
||||||
|
selectOption,
|
||||||
|
} from '@/testing-browser/Select';
|
||||||
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 { offsetSize } from 'support';
|
import { offsetSize } from 'support';
|
||||||
@@ -22,9 +26,10 @@ const changesSlot: HTMLButtonElement | null = document.querySelector('#changes')
|
|||||||
const preInitChildren = targetElm?.children.length;
|
const preInitChildren = targetElm?.children.length;
|
||||||
|
|
||||||
const trinsicObserver = createTrinsicObserver(targetElm as HTMLElement, (heightIntrinsicCache) => {
|
const trinsicObserver = createTrinsicObserver(targetElm as HTMLElement, (heightIntrinsicCache) => {
|
||||||
if (heightIntrinsicCache._changed) {
|
const [currentHeightIntrinsic, currentHeightIntrinsicChanged] = heightIntrinsicCache;
|
||||||
|
if (currentHeightIntrinsicChanged) {
|
||||||
heightIterations += 1;
|
heightIterations += 1;
|
||||||
heightIntrinsic = heightIntrinsicCache._value;
|
heightIntrinsic = currentHeightIntrinsic;
|
||||||
}
|
}
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (changesSlot) {
|
if (changesSlot) {
|
||||||
@@ -65,10 +70,14 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
|
|||||||
|
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
if (trinsicHeightChanged) {
|
if (trinsicHeightChanged) {
|
||||||
should.equal(heightIterations, currHeightIterations + 1, 'Height intrinsic change has been detected correctly.');
|
should.equal(
|
||||||
|
heightIterations,
|
||||||
|
currHeightIterations + 1,
|
||||||
|
'Height intrinsic change has been detected correctly.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
should.equal(
|
should.equal(
|
||||||
trinsicObserver._getCurrentCacheValues()._heightIntrinsic._value,
|
trinsicObserver._getCurrentCacheValues()._heightIntrinsic[0],
|
||||||
newHeightIntrinsic,
|
newHeightIntrinsic,
|
||||||
'Height intrinsic cache value is correct.'
|
'Height intrinsic cache value is correct.'
|
||||||
);
|
);
|
||||||
@@ -97,7 +106,11 @@ const changeWhileHidden = async () => {
|
|||||||
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
|
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
|
||||||
|
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
should.equal(heightIntrinsic, false, 'Trinsic sizing changes while hidden from intrinsic to extrinsic.');
|
should.equal(
|
||||||
|
heightIntrinsic,
|
||||||
|
false,
|
||||||
|
'Trinsic sizing changes while hidden from intrinsic to extrinsic.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -111,7 +124,11 @@ const changeWhileHidden = async () => {
|
|||||||
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
|
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
|
||||||
|
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
should.equal(heightIntrinsic, true, 'Trinsic sizing changes while hidden from extrinsic to intrinsic.');
|
should.equal(
|
||||||
|
heightIntrinsic,
|
||||||
|
true,
|
||||||
|
'Trinsic sizing changes while hidden from extrinsic to intrinsic.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -133,7 +150,11 @@ const start = async () => {
|
|||||||
await changeWhileHidden();
|
await changeWhileHidden();
|
||||||
|
|
||||||
trinsicObserver._destroy();
|
trinsicObserver._destroy();
|
||||||
should.equal(targetElm?.children.length, preInitChildren, 'After destruction all generated elements are removed.');
|
should.equal(
|
||||||
|
targetElm?.children.length,
|
||||||
|
preInitChildren,
|
||||||
|
'After destruction all generated elements are removed.'
|
||||||
|
);
|
||||||
setTestResult(true);
|
setTestResult(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
|
||||||
|
import { test } from '@playwright/test';
|
||||||
|
|
||||||
|
playwrightRollup();
|
||||||
|
|
||||||
|
test.describe('TrinsicObserver', () => {
|
||||||
|
test('with IntersectionObserver', async ({ page }) => {
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with ResizeObserver', async ({ page }) => {
|
||||||
|
await page.click('#ioPolyfill');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with ResizeObserver polyfill', async ({ page }) => {
|
||||||
|
await page.click('#ioPolyfill');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.click('#roPolyfill');
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.click('#start');
|
||||||
|
await expectSuccess(page);
|
||||||
|
});
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user