Loading core/misc/htmx/htmx-assets.js +8 −1 Original line number Diff line number Diff line Loading @@ -137,8 +137,15 @@ // @see https://htmx.org/events/#htmx:afterSettle htmx.on('htmx:afterSettle', ({ detail }) => { (requestAssetsLoaded.get(detail.xhr) || Promise.resolve()).then(() => { let processTarget = detail.elt.parentElement; // Some HTMX swaps put the incoming element before or after detail.elt. htmx.trigger(detail.elt.parentNode, 'htmx:drupal:load'); // We normally target the parent element so that we process the added // elements. When there is no parent element or detail.elt is the body, // we target the element itself. if (detail.elt === document.body || processTarget === null) { processTarget = detail.elt; } htmx.trigger(processTarget, 'htmx:drupal:load'); // This should be automatic but don't wait for the garbage collector. requestAssetsLoaded.delete(detail.xhr); }); Loading core/modules/system/tests/modules/test_htmx/src/Controller/HtmxTestAttachmentsController.php +21 −13 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ namespace Drupal\test_htmx\Controller; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\EventSubscriber\MainContentViewSubscriber; use Drupal\Core\Htmx\Htmx; use Drupal\Core\Url; Loading @@ -31,7 +30,7 @@ public function page(): array { * A render array. */ public function before(): array { return self::generateHtmxButton('beforebegin'); return self::generateHtmxButton(swap: 'beforebegin'); } /** Loading @@ -41,7 +40,7 @@ public function before(): array { * A render array. */ public function after(): array { return self::generateHtmxButton('afterend'); return self::generateHtmxButton(swap: 'afterend'); } /** Loading @@ -51,7 +50,24 @@ public function after(): array { * A render array. */ public function withWrapperFormat(): array { return self::generateHtmxButton('', TRUE); return self::generateHtmxButton(swap: '', useWrapperFormat: TRUE); } /** * Tests body targeting and swapping. * * @return mixed[] * A render array. */ public function selectBody(): array { return [ '#title' => $this->t('Boosted body'), '#type' => 'link', '#url' => Url::fromRoute('test_htmx.attachments.replace'), '#attributes' => [ 'class' => ['htmx-test-link'], ], ]; } /** Loading Loading @@ -92,15 +108,7 @@ public static function replaceWithAjax(): array { * The render array. */ public static function generateHtmxButton(string $swap = '', bool $useWrapperFormat = FALSE): array { $options = []; if ($useWrapperFormat) { $options = [ 'query' => [ MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_htmx', ], ]; } $url = Url::fromRoute('test_htmx.attachments.replace', [], $options); $url = Url::fromRoute('test_htmx.attachments.replace'); $build['replace'] = [ '#type' => 'html_tag', '#tag' => 'button', Loading core/modules/system/tests/modules/test_htmx/src/Hook/TestHtmxHooks.php 0 → 100644 +30 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\test_htmx\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Routing\RouteMatchInterface; /** * Hooks for the test_htmx module. */ class TestHtmxHooks { public function __construct( protected RouteMatchInterface $routeMatch, ) {} /** * Implements hook_preprocess_HOOK() for html. */ #[Hook('preprocess_html')] public function boost(array &$variables): void { if ($this->routeMatch->getRouteName() === 'test_htmx.attachments.body') { $variables['#attached']['library'][] = 'core/drupal.htmx'; $variables['attributes']['data-hx-boost'] = 'true'; } } } core/modules/system/tests/modules/test_htmx/test_htmx.routing.yml +8 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,14 @@ test_htmx.attachments.wrapper: requirements: _permission: 'access content' test_htmx.attachments.body: path: '/htmx-test-attachments/body' defaults: _title: 'Body testing' _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::selectBody' requirements: _permission: 'access content' test_htmx.attachments.replace: path: '/htmx-test-attachments/replace' defaults: Loading core/tests/Drupal/Nightwatch/Tests/htmx/htmxTest.js +23 −0 Original line number Diff line number Diff line Loading @@ -138,4 +138,27 @@ module.exports = { .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, 'Boosted Body': (browser) => { // Load the route htmx will use for the request on click and confirm the // markup we will be looking for is present in the source markup. browser .drupalRelativeURL('/htmx-test-attachments/replace') .waitForElementVisible('body', 1000) .assert.elementPresent(elementInitSelector); // Now load the page with the htmx enhanced link and verify the absence // of the markup to be inserted. Click the link // and check for inserted javascript and markup. browser .drupalRelativeURL('/htmx-test-attachments/body') .waitForElementVisible('body', 1000) .assert.not.elementPresent(scriptSelector) .assert.not.elementPresent(cssSelector) .waitForElementVisible('a.htmx-test-link', 1000) .click('a.htmx-test-link') .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, }; Loading
core/misc/htmx/htmx-assets.js +8 −1 Original line number Diff line number Diff line Loading @@ -137,8 +137,15 @@ // @see https://htmx.org/events/#htmx:afterSettle htmx.on('htmx:afterSettle', ({ detail }) => { (requestAssetsLoaded.get(detail.xhr) || Promise.resolve()).then(() => { let processTarget = detail.elt.parentElement; // Some HTMX swaps put the incoming element before or after detail.elt. htmx.trigger(detail.elt.parentNode, 'htmx:drupal:load'); // We normally target the parent element so that we process the added // elements. When there is no parent element or detail.elt is the body, // we target the element itself. if (detail.elt === document.body || processTarget === null) { processTarget = detail.elt; } htmx.trigger(processTarget, 'htmx:drupal:load'); // This should be automatic but don't wait for the garbage collector. requestAssetsLoaded.delete(detail.xhr); }); Loading
core/modules/system/tests/modules/test_htmx/src/Controller/HtmxTestAttachmentsController.php +21 −13 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ namespace Drupal\test_htmx\Controller; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\EventSubscriber\MainContentViewSubscriber; use Drupal\Core\Htmx\Htmx; use Drupal\Core\Url; Loading @@ -31,7 +30,7 @@ public function page(): array { * A render array. */ public function before(): array { return self::generateHtmxButton('beforebegin'); return self::generateHtmxButton(swap: 'beforebegin'); } /** Loading @@ -41,7 +40,7 @@ public function before(): array { * A render array. */ public function after(): array { return self::generateHtmxButton('afterend'); return self::generateHtmxButton(swap: 'afterend'); } /** Loading @@ -51,7 +50,24 @@ public function after(): array { * A render array. */ public function withWrapperFormat(): array { return self::generateHtmxButton('', TRUE); return self::generateHtmxButton(swap: '', useWrapperFormat: TRUE); } /** * Tests body targeting and swapping. * * @return mixed[] * A render array. */ public function selectBody(): array { return [ '#title' => $this->t('Boosted body'), '#type' => 'link', '#url' => Url::fromRoute('test_htmx.attachments.replace'), '#attributes' => [ 'class' => ['htmx-test-link'], ], ]; } /** Loading Loading @@ -92,15 +108,7 @@ public static function replaceWithAjax(): array { * The render array. */ public static function generateHtmxButton(string $swap = '', bool $useWrapperFormat = FALSE): array { $options = []; if ($useWrapperFormat) { $options = [ 'query' => [ MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_htmx', ], ]; } $url = Url::fromRoute('test_htmx.attachments.replace', [], $options); $url = Url::fromRoute('test_htmx.attachments.replace'); $build['replace'] = [ '#type' => 'html_tag', '#tag' => 'button', Loading
core/modules/system/tests/modules/test_htmx/src/Hook/TestHtmxHooks.php 0 → 100644 +30 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\test_htmx\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Routing\RouteMatchInterface; /** * Hooks for the test_htmx module. */ class TestHtmxHooks { public function __construct( protected RouteMatchInterface $routeMatch, ) {} /** * Implements hook_preprocess_HOOK() for html. */ #[Hook('preprocess_html')] public function boost(array &$variables): void { if ($this->routeMatch->getRouteName() === 'test_htmx.attachments.body') { $variables['#attached']['library'][] = 'core/drupal.htmx'; $variables['attributes']['data-hx-boost'] = 'true'; } } }
core/modules/system/tests/modules/test_htmx/test_htmx.routing.yml +8 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,14 @@ test_htmx.attachments.wrapper: requirements: _permission: 'access content' test_htmx.attachments.body: path: '/htmx-test-attachments/body' defaults: _title: 'Body testing' _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::selectBody' requirements: _permission: 'access content' test_htmx.attachments.replace: path: '/htmx-test-attachments/replace' defaults: Loading
core/tests/Drupal/Nightwatch/Tests/htmx/htmxTest.js +23 −0 Original line number Diff line number Diff line Loading @@ -138,4 +138,27 @@ module.exports = { .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, 'Boosted Body': (browser) => { // Load the route htmx will use for the request on click and confirm the // markup we will be looking for is present in the source markup. browser .drupalRelativeURL('/htmx-test-attachments/replace') .waitForElementVisible('body', 1000) .assert.elementPresent(elementInitSelector); // Now load the page with the htmx enhanced link and verify the absence // of the markup to be inserted. Click the link // and check for inserted javascript and markup. browser .drupalRelativeURL('/htmx-test-attachments/body') .waitForElementVisible('body', 1000) .assert.not.elementPresent(scriptSelector) .assert.not.elementPresent(cssSelector) .waitForElementVisible('a.htmx-test-link', 1000) .click('a.htmx-test-link') .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, };