Skip to content
Snippets Groups Projects
Unverified Commit f8c91fdd authored by Lauri Timmanee's avatar Lauri Timmanee
Browse files

Issue #3256768 by mherchel, nod_, andregp, andy-blum: Drupal.displace() should...

Issue #3256768 by mherchel, nod_, andregp, andy-blum: Drupal.displace() should set CSS Variables indicating displacement values
parent e0730251
No related branches found
No related tags found
37 merge requests!12227Issue #3181946 by jonmcl, mglaman,!7471uncessary 5 files are moved from media-library folder to misc folder,!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!4868Issue #1428520: Improve menu parent link selection,!4289Issue #1344552 by marcingy, Niklas Fiekas, Ravi.J, aleevas, Eduardo Morales...,!4114Issue #2707291: Disable body-level scrolling when a dialog is open as a modal,!3630Issue #2815301 by Chi, DanielVeza, kostyashupenko, smustgrave: Allow to create...,!3291Issue #3336463: Rewrite rules for gzipped CSS and JavaScript aggregates never match,!3143Issue #3313342: [PHP 8.1] Deprecated function: strpos(): Passing null to parameter #1 LayoutBuilderUiCacheContext.php on line 28,!3102Issue #3164428 by DonAtt, longwave, sahil.goyal, Anchal_gupta, alexpott: Use...,!2853#3274419 Makes BaseFieldOverride inherit the internal property from the base field.,!2719Issue #3110137: Remove Classy from core.,!2437Issue #3238257 by hooroomoo, Wim Leers: Fragment link pointing to <textarea>...,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2074Issue #2707689: NodeForm::actions() checks for delete access on new entities,!2062Issue #3246454: Add weekly granularity to views date sort,!1591Issue #3199697: Add JSON:API Translation experimental module,!1484Exposed filters get values from URL when Ajax is on,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1254Issue #3238915: Refactor (if feasible) uses of the jQuery ready function to use VanillaJS,!1162Issue #3100350: Unable to save '/' root path alias,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!957Added throwing of InvalidPluginDefinitionException from getDefinition().,!925Issue #2339235: Remove taxonomy hard dependency on node module,!877Issue #2708101: Default value for link text is not saved,!873Issue #2875228: Site install not using batch API service,!872Draft: Issue #3221319: Race condition when creating menu links and editing content deletes menu links,!844Resolve #3036010 "Updaters",!712Issue #2909128: Autocomplete intermittent on Chrome Android,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493,!485Sets the autocomplete attribute for username/password input field on login form.,!30Issue #3182188: Updates composer usage to point at ./vendor/bin/composer
......@@ -23,25 +23,66 @@
*
* @event drupalViewportOffsetChange
*/
(function ($, Drupal, debounce) {
/**
* @name Drupal.displace.offsets
*
* @type {Drupal~displaceOffset}
*/
let offsets = {
top: 0,
const cache = {
right: 0,
bottom: 0,
left: 0,
bottom: 0,
top: 0,
};
/**
* The prefix used for the css custom variable name.
*
* @type {string}
*/
const cssVarPrefix = '--drupal-displace-offset';
const documentStyle = document.documentElement.style;
const offsetKeys = Object.keys(cache);
/**
* The object with accessors that update the CSS variable on value update.
*
* @type {Drupal~displaceOffset}
*/
const offsetProps = {};
offsetKeys.forEach((edge) => {
offsetProps[edge] = {
// Show this property when using Object.keys().
enumerable: true,
get() {
return cache[edge];
},
set(value) {
// Only update the CSS custom variable when the value changed.
if (value !== cache[edge]) {
documentStyle.setProperty(`${cssVarPrefix}-${edge}`, `${value}px`);
}
cache[edge] = value;
},
};
});
/**
* Current value of the size of margins on the page.
*
* This property is read-only and the object is sealed to prevent key name
* modifications since key names are used to dynamically construct CSS custom
* variable names.
*
* @name Drupal.displace.offsets
*
* @type {Drupal~displaceOffset}
*/
const offsets = Object.seal(Object.defineProperties({}, offsetProps));
/**
* Calculates displacement for element based on its dimensions and placement.
*
* @param {HTMLElement} el
* The jQuery element whose dimensions and placement will be measured.
* The element whose dimensions and placement will be measured.
*
* @param {string} edge
* The name of the edge of the viewport that the element is associated
......@@ -139,33 +180,23 @@
return edgeOffset;
}
/**
* Determines the viewport offsets.
*
* @return {Drupal~displaceOffset}
* An object whose keys are the for sides an element -- top, right, bottom
* and left. The value of each key is the viewport displacement distance for
* that edge.
*/
function calculateOffsets() {
return {
top: calculateOffset('top'),
right: calculateOffset('right'),
bottom: calculateOffset('bottom'),
left: calculateOffset('left'),
};
}
/**
* Informs listeners of the current offset dimensions.
*
* Corresponding CSS custom variables are also updated.
* Corresponding CSS custom variables names are:
* - `--drupal-displace-offset-top`
* - `--drupal-displace-offset-right`
* - `--drupal-displace-offset-bottom`
* - `--drupal-displace-offset-left`
*
* @function Drupal.displace
*
* @prop {Drupal~displaceOffset} offsets
*
* @param {bool} [broadcast]
* When true or undefined, causes the recalculated offsets values to be
* broadcast to listeners.
* @param {bool} [broadcast=true]
* When true, causes the recalculated offsets values to be
* broadcast to listeners. If none is given, defaults to true.
*
* @return {Drupal~displaceOffset}
* An object whose keys are the for sides an element -- top, right, bottom
......@@ -174,10 +205,20 @@
*
* @fires event:drupalViewportOffsetChange
*/
function displace(broadcast) {
offsets = calculateOffsets();
Drupal.displace.offsets = offsets;
if (typeof broadcast === 'undefined' || broadcast) {
function displace(broadcast = true) {
const newOffsets = {};
// Getting the offset and setting the offset needs to be separated because
// of performance concerns. Only do DOM/style reading happening here.
offsetKeys.forEach((edge) => {
newOffsets[edge] = calculateOffset(edge);
});
// Once we have all the values, write to the DOM/style.
offsetKeys.forEach((edge) => {
// Updating the value in place also update Drupal.displace.offsets.
offsets[edge] = newOffsets[edge];
});
if (broadcast) {
$(document).trigger('drupalViewportOffsetChange', offsets);
}
return offsets;
......@@ -195,7 +236,6 @@
return;
}
this.displaceProcessed = true;
$(window).on('resize.drupalDisplace', debounce(displace, 200));
},
};
......@@ -206,19 +246,22 @@
* @ignore
*/
Drupal.displace = displace;
$.extend(Drupal.displace, {
/**
* Expose offsets to other scripts to avoid having to recalculate offsets.
*
* @ignore
*/
offsets,
/**
* Expose method to compute a single edge offsets.
*
* @ignore
*/
calculateOffset,
/**
* Expose offsets to other scripts to avoid having to recalculate offsets.
*
* @ignore
*/
Object.defineProperty(Drupal.displace, 'offsets', {
value: offsets,
// Make sure other scripts don't replace this object.
writable: false,
});
/**
* Expose method to compute a single edge offsets.
*
* @ignore
*/
Drupal.displace.calculateOffset = calculateOffset;
})(jQuery, Drupal, Drupal.debounce);
......@@ -6,12 +6,35 @@
**/
(function ($, Drupal, debounce) {
let offsets = {
top: 0,
const cache = {
right: 0,
left: 0,
bottom: 0,
left: 0
top: 0
};
const cssVarPrefix = '--drupal-displace-offset';
const documentStyle = document.documentElement.style;
const offsetKeys = Object.keys(cache);
const offsetProps = {};
offsetKeys.forEach(edge => {
offsetProps[edge] = {
enumerable: true,
get() {
return cache[edge];
},
set(value) {
if (value !== cache[edge]) {
documentStyle.setProperty(`${cssVarPrefix}-${edge}`, `${value}px`);
}
cache[edge] = value;
}
};
});
const offsets = Object.seal(Object.defineProperties({}, offsetProps));
function getRawOffset(el, edge) {
const $el = $(el);
......@@ -69,20 +92,17 @@
return edgeOffset;
}
function calculateOffsets() {
return {
top: calculateOffset('top'),
right: calculateOffset('right'),
bottom: calculateOffset('bottom'),
left: calculateOffset('left')
};
}
function displace(broadcast) {
offsets = calculateOffsets();
Drupal.displace.offsets = offsets;
if (typeof broadcast === 'undefined' || broadcast) {
function displace() {
let broadcast = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
const newOffsets = {};
offsetKeys.forEach(edge => {
newOffsets[edge] = calculateOffset(edge);
});
offsetKeys.forEach(edge => {
offsets[edge] = newOffsets[edge];
});
if (broadcast) {
$(document).trigger('drupalViewportOffsetChange', offsets);
}
......@@ -101,8 +121,9 @@
};
Drupal.displace = displace;
$.extend(Drupal.displace, {
offsets,
calculateOffset
Object.defineProperty(Drupal.displace, 'offsets', {
value: offsets,
writable: false
});
Drupal.displace.calculateOffset = calculateOffset;
})(jQuery, Drupal, Drupal.debounce);
\ No newline at end of file
name: 'JS Displace Tests'
type: module
description: 'Support module for testing Drupal.displace() JavaScript API.'
package: Testing
version: VERSION
<?php
/**
* @file
* Functions to support testing Drupal.displace() JavaScript API.
*/
/**
* Implements hook_preprocess_html().
*/
function js_displace_preprocess_html(&$variables) {
$variables['#attached']['library'][] = 'core/drupal.displace';
}
const testValues = {
top: 200,
right: 110,
bottom: 145,
left: 310,
};
const testElements = `
<div
data-offset-top
style="
background-color: red;
height: 110px;
left: 0;
position: fixed;
right: 0;
top: 90px;
width: 100%;"
></div>
<div
data-offset-right
style="
background-color: blue;
bottom: 0;
height: 100%;
position: fixed;
right: 10px;
top: 0;
width: 100px;"
></div>
<div
data-offset-bottom
style="
background-color: yellow;
bottom: 45px;
height: 100px;
left: 0;
position: fixed;
right: 0;
width: 100%;"
></div>
<div
data-offset-left
style="
background-color: orange;
bottom: 0;
height: 100%;
left: 10px;
position: fixed;
top: 0;
width: 300px;"
></div>
`;
module.exports = {
'@tags': ['core'],
before(browser) {
browser.drupalInstall().drupalInstallModule('js_displace');
},
after(browser) {
browser.drupalUninstall();
},
'Test Drupal.displace() JavaScript API': (browser) => {
browser
.drupalRelativeURL('/')
.waitForElementVisible('body')
.execute(
// eslint-disable-next-line func-names, prefer-arrow-callback, no-shadow
function (testValues, testElements) {
const testElementsContainer = document.createElement('div');
testElementsContainer.innerHTML = testElements;
document.body.append(testElementsContainer);
const displaceOutput = Drupal.displace();
return (
displaceOutput.top === testValues.top &&
displaceOutput.right === testValues.right &&
displaceOutput.bottom === testValues.bottom &&
displaceOutput.left === testValues.left
);
},
[testValues, testElements],
(result) => {
browser.assert.ok(
result.value,
'Drupal.displace() JS returns proper offsets for all edges.',
);
},
)
.execute(
// eslint-disable-next-line func-names, prefer-arrow-callback, no-shadow
function (testValues) {
const rootStyles = getComputedStyle(document.documentElement);
const topOffsetStyle = rootStyles.getPropertyValue(
'--drupal-displace-offset-top',
);
const rightOffsetStyle = rootStyles.getPropertyValue(
'--drupal-displace-offset-right',
);
const bottomOffsetStyle = rootStyles.getPropertyValue(
'--drupal-displace-offset-bottom',
);
const leftOffsetStyle = rootStyles.getPropertyValue(
'--drupal-displace-offset-left',
);
return (
topOffsetStyle === `${testValues.top}px` &&
rightOffsetStyle === `${testValues.right}px` &&
bottomOffsetStyle === `${testValues.bottom}px` &&
leftOffsetStyle === `${testValues.left}px`
);
},
[testValues],
(result) => {
browser.assert.ok(
result.value,
'Drupal.displace() properly sets CSS variables.',
);
},
);
},
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment