Skip to content
Snippets Groups Projects
Verified Commit 6cfd0670 authored by Dave Long's avatar Dave Long
Browse files

Issue #2707291 by Utkarsh_33, lauriii, darvanen, samuel.mortenson, hooroomoo,...

Issue #2707291 by Utkarsh_33, lauriii, darvanen, samuel.mortenson, hooroomoo, komalk, narendraR, Vidushi Mehta, sasanikolic, droplet, smustgrave, yoroy, Bcwald, kostyashupenko, penyaskito: Disable body-level scrolling when a dialog is open as a modal
parent 9a1ce8c4
Branches
Tags
45 merge requests!12227Issue #3181946 by jonmcl, mglaman,!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!4868Issue #1428520: Improve menu parent link selection,!4594Applying patch for Views Global Text area field to allow extra HTML tags. As video, source and iframe tag is not rendering. Due to which Media embedded video and remote-video not rendering in Views Global Text area field.,!3878Removed unused condition head title for views,!38582585169-10.1.x,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3668Resolve #3347842 "Deprecate the trusted",!3651Issue #3347736: Create new SDC component for Olivero (header-search),!3546refactored dialog.pcss file,!3531Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!3502Issue #3335308: Confusing behavior with FormState::setFormState and FormState::setMethod,!3478Issue #3337882: Deleted menus are not removed from content type config,!3452Issue #3332701: Refactor Claro's tablesort-indicator stylesheet,!3451Issue #2410579: Allows setting the current language programmatically.,!3355Issue #3209129: Scrolling problems when adding a block via layout builder,!3226Issue #2987537: Custom menu link entity type should not declare "bundle" entity key,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3147Issue #3328457: Replace most substr($a, $i) where $i is negative with str_ends_with(),!3146Issue #3328456: Replace substr($a, 0, $i) with str_starts_with(),!3133core/modules/system/css/components/hidden.module.css,!31312878513-10.1.x,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2614Issue #2981326: Replace non-test usages of \Drupal::logger() with IoC injection,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2334Issue #3228209: Add hasRole() method to AccountInterface,!2062Issue #3246454: Add weekly granularity to views date sort,!1591Issue #3199697: Add JSON:API Translation experimental module,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1105Issue #3025039: New non translatable field on translatable content throws error,!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,!877Issue #2708101: Default value for link text is not saved,!844Resolve #3036010 "Updaters",!673Issue #3214208: FinishResponseSubscriber could create duplicate headers,!617Issue #3043725: Provide a Entity Handler for user cancelation,!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
......@@ -25,7 +25,8 @@
"once": true,
"CKEditor5": true,
"tabbable": true,
"slugify": true
"slugify": true,
"bodyScrollLock" : true
},
"rules": {
"prettier/prettier": "error",
......
/**
* tua-body-scroll-lock v1.4.0
* (c) 2023 Evinma, BuptStEve
* @license MIT
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).bodyScrollLock={})}(this,(function(e){"use strict";var t=function(){return"undefined"==typeof window},o=function(e){e=e||navigator.userAgent;var t=/(iPad).*OS\s([\d_]+)/.test(e);return{ios:!t&&/(iPhone\sOS)\s([\d_]+)/.test(e)||t,android:/(Android);?[\s/]+([\d.]+)?/.test(e)}};var n=0,i=0,r=0,s=null,l=!1,c=[],d=function(e){if(t())return!1;if(!e)throw new Error("options must be provided");var o=!1,n={get passive(){o=!0}},i=function(){},r="__TUA_BSL_TEST_PASSIVE__";window.addEventListener(r,i,n),window.removeEventListener(r,i,n);var s=e.capture;return o?e:void 0!==s&&s}({passive:!1}),u=!t()&&"scrollBehavior"in document.documentElement.style,f=function(e){e.cancelable&&e.preventDefault()};e.clearBodyLocks=function(){if(!t())if(n=0,o().ios||"function"!=typeof s){if(c.length)for(var e=c.pop();e;)e.ontouchmove=null,e.ontouchstart=null,e=c.pop();l&&(document.removeEventListener("touchmove",f,d),l=!1)}else s()},e.lock=function(e,a){if(!t()){if(o().ios){if(e)(Array.isArray(e)?e:[e]).forEach((function(e){e&&-1===c.indexOf(e)&&(e.ontouchstart=function(e){i=e.targetTouches[0].clientY,r=e.targetTouches[0].clientX},e.ontouchmove=function(t){1===t.targetTouches.length&&function(e,t){if(t){var o=t.scrollTop,n=t.scrollLeft,s=t.scrollWidth,l=t.scrollHeight,c=t.clientWidth,d=t.clientHeight,u=e.targetTouches[0].clientX-r,a=e.targetTouches[0].clientY-i,h=Math.abs(a)>Math.abs(u);if(h&&(a>0&&0===o||a<0&&o+d+1>=l)||!h&&(u>0&&0===n||u<0&&n+c+1>=s))return f(e)}e.stopPropagation()}(t,e)},c.push(e))}));l||(document.addEventListener("touchmove",f,d),l=!0)}else n<=0&&(s=o().android?function(e){var t=document.documentElement,o=document.body,n=t.scrollTop||o.scrollTop,i=Object.assign({},t.style),r=Object.assign({},o.style);return t.style.height="100%",t.style.overflow="hidden",o.style.top="-".concat(n,"px"),o.style.width="100%",o.style.height="auto",o.style.position="fixed",o.style.overflow=(null==e?void 0:e.overflowType)||"hidden",function(){t.style.height=i.height||"",t.style.overflow=i.overflow||"",["top","width","height","overflow","position"].forEach((function(e){o.style[e]=r[e]||""}));var e={top:n,behavior:"instant"};u?window.scrollTo(e):window.scrollTo(0,n)}}(a):(h=document.documentElement,v=Object.assign({},h.style),p=window.innerWidth-h.clientWidth,y=parseInt(window.getComputedStyle(h).paddingRight,10),h.style.overflow="hidden",h.style.boxSizing="border-box",h.style.paddingRight="".concat(p+y,"px"),function(){["overflow","boxSizing","paddingRight"].forEach((function(e){h.style[e]=v[e]||""}))}));var h,v,p,y;n+=1}},e.unlock=function(e){if(!(t()||(n-=1)>0))if(o().ios||"function"!=typeof s){if(e)(Array.isArray(e)?e:[e]).forEach((function(e){var t=c.indexOf(e);-1!==t&&(e.ontouchmove=null,e.ontouchstart=null,c.splice(t,1))}));l&&(document.removeEventListener("touchmove",f,d),l=!1)}else s()}}));
......@@ -534,6 +534,7 @@ drupal.dialog:
assets/vendor/jquery.ui/ui/safe-blur-min.js: { weight: -11.8, minified: true }
assets/vendor/jquery.ui/ui/widget-min.js: { weight: -11.8, minified: true }
assets/vendor/jquery.ui/ui/version-min.js: { weight: -11.9, minified: true }
assets/vendor/tua-body-scroll-lock/tua-bsl.umd.min.js: { weight: -1, minified: true }
# All CSS assets previously came from core/jquery.ui, a deprecated library.
# @todo replace with solution found in https://drupal.org/node/2158943
css:
......
......@@ -5,7 +5,7 @@
* @see http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#the-dialog-element
*/
(function ($, Drupal, drupalSettings) {
(function ($, Drupal, drupalSettings, bodyScrollLock) {
/**
* Default dialog options.
*
......@@ -73,11 +73,21 @@
$(window).trigger('dialog:beforecreate', [dialog, $element, settings]);
$element.dialog(settings);
dialog.open = true;
// Locks the body scroll only when it opens in modal.
if (settings.modal) {
// Locks the body when the dialog opens.
bodyScrollLock.lock($element.get(0));
}
$(window).trigger('dialog:aftercreate', [dialog, $element, settings]);
}
function closeDialog(value) {
$(window).trigger('dialog:beforeclose', [dialog, $element]);
// Unlocks the body when the dialog closes.
bodyScrollLock.unlock($element.get(0));
$element.dialog('close');
dialog.returnValue = value;
dialog.open = false;
......@@ -94,4 +104,4 @@
return dialog;
};
})(jQuery, Drupal, drupalSettings);
})(jQuery, Drupal, drupalSettings, bodyScrollLock);
......@@ -61,7 +61,7 @@ public function testBlockFilter() {
// Test Drupal.announce() message when multiple matches are expected.
$expected_message = count($visible_rows) . ' blocks are available in the modified list.';
$assertSession->elementTextContains('css', '#drupal-live-announce', $expected_message);
$this->assertAnnounceContains($expected_message);
// Test Drupal.announce() message when only one match is expected.
$filter->setValue('Powered by');
......@@ -69,7 +69,7 @@ public function testBlockFilter() {
$visible_rows = $this->filterVisibleElements($block_rows);
$this->assertCount(1, $visible_rows);
$expected_message = '1 block is available in the modified list.';
$assertSession->elementTextContains('css', '#drupal-live-announce', $expected_message);
$this->assertAnnounceContains($expected_message);
// Test Drupal.announce() message when no matches are expected.
$filter->setValue('Pan-Galactic Gargle Blaster');
......@@ -77,7 +77,7 @@ public function testBlockFilter() {
$visible_rows = $this->filterVisibleElements($block_rows);
$this->assertCount(0, $visible_rows);
$expected_message = '0 blocks are available in the modified list.';
$assertSession->elementTextContains('css', '#drupal-live-announce', $expected_message);
$this->assertAnnounceContains($expected_message);
}
/**
......@@ -95,4 +95,17 @@ protected function filterVisibleElements(array $elements) {
return $elements;
}
/**
* Checks for inclusion of text in #drupal-live-announce.
*
* @param string $expected_message
* The text expected to be present in #drupal-live-announce.
*
* @internal
*/
protected function assertAnnounceContains(string $expected_message): void {
$assert_session = $this->assertSession();
$this->assertNotEmpty($assert_session->waitForElement('css', "#drupal-live-announce:contains('$expected_message')"));
}
}
......@@ -155,7 +155,7 @@ public function testWidget() {
// Insert media to test validation with null target_bundles.
$this->switchToMediaType('One');
$this->assertNotEmpty($assert_session->waitForText('Showing Type One media.'));
$this->assertAnnounceContains('Showing Type One media.');
$this->selectMediaItem(0);
$this->pressInsertSelected('Added one media item.');
......@@ -207,12 +207,12 @@ public function testWidget() {
// Assert the announcements for media type navigation in the media library.
$this->openMediaLibraryForField('field_unlimited_media');
$this->switchToMediaType('Three');
$this->assertNotEmpty($assert_session->waitForText('Showing Type Three media.'));
$this->assertAnnounceContains('Showing Type Three media.');
$this->switchToMediaType('One');
$this->assertNotEmpty($assert_session->waitForText('Showing Type One media.'));
$this->assertAnnounceContains('Showing Type One media.');
// Assert the links can be triggered by via the spacebar.
$assert_session->elementExists('named', ['link', 'Type Three'])->keyPress(32);
$this->waitForText('Showing Type Three media.');
$this->assertAnnounceContains('Showing Type Three media.');
$assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click();
// Assert media is only visible on the tab for the related media type.
......@@ -221,7 +221,7 @@ public function testWidget() {
$assert_session->pageTextContains('Bear');
$assert_session->pageTextNotContains('Turtle');
$this->switchToMediaType('Three');
$this->assertNotEmpty($assert_session->waitForText('Showing Type Three media.'));
$this->assertAnnounceContains('Showing Type Three media.');
$assert_session->elementExists('named', ['link', 'Show Type Three media (selected)']);
$assert_session->pageTextNotContains('Dog');
$assert_session->pageTextNotContains('Bear');
......@@ -561,6 +561,19 @@ public function testAddAfterReordering(): void {
$assert_session->elementTextContains('css', '.field--name-field-unlimited-media > .field__items > .field__item:last-child', 'Bear');
}
/**
* Checks for inclusion of text in #drupal-live-announce.
*
* @param string $expected_message
* The text that is expected to be present in the #drupal-live-announce element.
*
* @internal
*/
protected function assertAnnounceContains(string $expected_message): void {
$assert_session = $this->assertSession();
$this->assertNotEmpty($assert_session->waitForElement('css', "#drupal-live-announce:contains('$expected_message')"));
}
/**
* {@inheritdoc}
*/
......
......@@ -422,8 +422,8 @@ protected function deselectMediaItem(int $index): void {
protected function switchToMediaLibraryGrid() {
$this->getSession()->getPage()->clickLink('Grid');
// Assert the display change is correctly announced for screen readers.
$this->waitForText('Loading grid view.');
$this->waitForText('Changed to grid view.');
$this->assertAnnounceContains('Loading grid view.');
$this->assertAnnounceContains('Changed to grid view.');
$this->assertMediaLibraryGrid();
}
......@@ -434,9 +434,9 @@ protected function switchToMediaLibraryTable() {
hold_test_response(TRUE);
$this->getSession()->getPage()->clickLink('Table');
// Assert the display change is correctly announced for screen readers.
$this->waitForText('Loading table view.');
$this->assertAnnounceContains('Loading table view.');
hold_test_response(FALSE);
$this->waitForText('Changed to table view.');
$this->assertAnnounceContains('Changed to table view.');
$this->assertMediaLibraryTable();
}
......@@ -473,4 +473,17 @@ protected function assertSelectedMediaCount($text) {
$this->assertSame($text, $selected_count->getText());
}
/**
* Checks for inclusion of text in #drupal-live-announce.
*
* @param string $expected_message
* The text expected to be present in #drupal-live-announce.
*
* @internal
*/
protected function assertAnnounceContains(string $expected_message): void {
$assert_session = $this->assertSession();
$this->assertNotEmpty($assert_session->waitForElement('css', "#drupal-live-announce:contains('$expected_message')"));
}
}
......@@ -3,7 +3,7 @@
* Views dialog behaviors.
*/
(function ($, Drupal, drupalSettings) {
(function ($, Drupal, drupalSettings, bodyScrollLock) {
function handleDialogResize(e) {
const $modal = $(e.currentTarget);
const $viewsOverride = $modal.find('[data-drupal-views-offset]');
......@@ -70,4 +70,39 @@
}
},
};
})(jQuery, Drupal, drupalSettings);
/**
* Binds a listener on dialog creation to handle Views modal scroll.
*
* @param {jQuery.Event} e
* The event triggered.
* @param {Drupal.dialog~dialogDefinition} dialog
* The dialog instance.
* @param {jQuery} $element
* The jQuery collection of the dialog element.
*/
$(window).on('dialog:aftercreate', (e, dialog, $element) => {
const $scroll = $element.find('.scroll');
if ($scroll.length) {
bodyScrollLock.unlock($element.get(0));
bodyScrollLock.lock($scroll.get(0));
}
});
/**
* Binds a listener on dialog close to handle Views modal scroll.
*
* @param {jQuery.Event} e
* The event triggered.
* @param {Drupal.dialog~dialogDefinition} dialog
* The dialog instance.
* @param {jQuery} $element
* The jQuery collection of the dialog element.
*/
$(window).on('dialog:beforeclose', (e, dialog, $element) => {
const $scroll = $element.find('.scroll');
if ($scroll.length) {
bodyScrollLock.unlock($scroll.get(0));
}
});
})(jQuery, Drupal, drupalSettings, bodyScrollLock);
......@@ -15,6 +15,7 @@ views_ui.admin:
- core/drupal.dropbutton
- views/views.ajax
- views_ui/admin.styling
- core/drupal.dialog
views_ui.listing:
version: VERSION
......
......@@ -94,6 +94,7 @@
"tabbable": "^6.1.2",
"terser": "^5.19.0",
"terser-webpack-plugin": "^5.3.9",
"tua-body-scroll-lock": "^1.4.0",
"transliteration": "^2.3.5",
"underscore": "~1.13.4",
"webpack": "^5.85.1",
......
......@@ -141,6 +141,12 @@ const assetsFolder = `${coreFolder}/assets/vendor`;
pack: 'loadjs',
files: [{ from: 'dist/loadjs.min.js', to: 'loadjs.min.js' }],
},
{
pack: 'tua-body-scroll-lock',
files: [
{ from: 'dist/tua-bsl.umd.min.js', to: 'tua-bsl.umd.min.js' },
],
},
{
pack: 'transliteration',
files: [
......
......@@ -5171,6 +5171,11 @@ tsconfig-paths@^3.14.1:
minimist "^1.2.6"
strip-bom "^3.0.0"
tua-body-scroll-lock@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/tua-body-scroll-lock/-/tua-body-scroll-lock-1.4.0.tgz#428fa6ec0cbaf9c8c68f95726e70144f7aeffdde"
integrity sha512-pbl411d7RSxjYXt5pB5GdBQ5BraVg1PMvr8BSUXc4buCMeK1OYu7L10PuOU8EhdfWoxGiAfvLrioSQe6Cr9m1g==
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment