Loading core/misc/htmx/htmx-assets.js +2 −1 Original line number Diff line number Diff line Loading @@ -166,7 +166,8 @@ // @see https://htmx.org/events/#htmx:afterSettle htmx.on('htmx:afterSettle', ({ detail }) => { requestAssetsLoaded.get(detail.xhr).then(() => { htmx.trigger(detail.elt, 'htmx:drupal:load'); // Some HTMX swaps put the incoming element before or after detail.elt. htmx.trigger(detail.elt.parentNode, '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 +34 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,26 @@ public function page(): array { return self::generateHtmxButton(); } /** * Builds a response with a `beforebegin` swap. * * @return mixed[] * A render array. */ public function before(): array { return self::generateHtmxButton('beforebegin'); } /** * Builds a response with an `afterend` swap.. * * @return mixed[] * A render array. */ public function after(): array { return self::generateHtmxButton('afterend'); } /** * Builds the HTMX response. * Loading @@ -43,13 +63,23 @@ public function replace(): array { return $build; } /** * We need a static callback that ignores callback parameters. * * @return array * The render array. */ public static function replaceWithAjax(): array { return static::generateHtmxButton(); } /** * Static helper to for reusable render array. * * @return array * The render array. */ public static function generateHtmxButton(): array { public static function generateHtmxButton(string $swap = ''): array { $url = Url::fromRoute('test_htmx.attachments.replace'); $build['replace'] = [ '#type' => 'html_tag', Loading @@ -68,6 +98,9 @@ public static function generateHtmxButton(): array { ], ], ]; if ($swap !== '') { $build['replace']['#attributes']['data-hx-swap'] = $swap; } $build['content'] = [ '#type' => 'container', Loading core/modules/system/tests/modules/test_htmx/src/Form/HtmxTestAjaxForm.php +1 −1 Original line number Diff line number Diff line Loading @@ -32,7 +32,7 @@ public function buildForm(array $form, FormStateInterface $form_state): array { '#ajax' => [ 'callback' => [ HtmxTestAttachmentsController::class, 'generateHtmxButton', 'replaceWithAjax', ], 'wrapper' => 'ajax-test-container', ], Loading core/modules/system/tests/modules/test_htmx/test_htmx.routing.yml +16 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,22 @@ test_htmx.attachments.page: requirements: _permission: 'access content' test_htmx.attachments.before: path: '/htmx-test-attachments/before' defaults: _title: 'Page' _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::before' requirements: _permission: 'access content' test_htmx.attachments.after: path: '/htmx-test-attachments/after' defaults: _title: 'Page' _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::after' requirements: _permission: 'access content' test_htmx.attachments.replace: path: '/htmx-test-attachments/replace' defaults: Loading core/tests/Drupal/Nightwatch/Tests/htmx/htmxTest.js +50 −4 Original line number Diff line number Diff line Loading @@ -41,8 +41,54 @@ module.exports = { .assert.not.elementPresent(cssSelector) .waitForElementVisible('[name="replace"]', 1000) .click('[name="replace"]') .waitForElementVisible(elementSelector, 6000) .waitForElementVisible(elementInitSelector, 6000) .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, 'Swap Before': (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 button and verify the absence // of the markup to be inserted. Click the button // and check for inserted javascript and markup. browser .drupalRelativeURL('/htmx-test-attachments/before') .waitForElementVisible('body', 1000) .assert.not.elementPresent(scriptSelector) .assert.not.elementPresent(cssSelector) .waitForElementVisible('[name="replace"]', 1000) .click('[name="replace"]') .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, 'Swap After': (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 button and verify the absence // of the markup to be inserted. Click the button // and check for inserted javascript and markup. browser .drupalRelativeURL('/htmx-test-attachments/after') .waitForElementVisible('body', 1000) .assert.not.elementPresent(scriptSelector) .assert.not.elementPresent(cssSelector) .waitForElementVisible('[name="replace"]', 1000) .click('[name="replace"]') .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, Loading @@ -69,8 +115,8 @@ module.exports = { .waitForElementVisible('[name="replace"]', 1000) .pause(1000) .click('[name="replace"]') .waitForElementVisible(elementSelector, 6000) .waitForElementVisible(elementInitSelector, 6000) .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, Loading Loading
core/misc/htmx/htmx-assets.js +2 −1 Original line number Diff line number Diff line Loading @@ -166,7 +166,8 @@ // @see https://htmx.org/events/#htmx:afterSettle htmx.on('htmx:afterSettle', ({ detail }) => { requestAssetsLoaded.get(detail.xhr).then(() => { htmx.trigger(detail.elt, 'htmx:drupal:load'); // Some HTMX swaps put the incoming element before or after detail.elt. htmx.trigger(detail.elt.parentNode, '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 +34 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,26 @@ public function page(): array { return self::generateHtmxButton(); } /** * Builds a response with a `beforebegin` swap. * * @return mixed[] * A render array. */ public function before(): array { return self::generateHtmxButton('beforebegin'); } /** * Builds a response with an `afterend` swap.. * * @return mixed[] * A render array. */ public function after(): array { return self::generateHtmxButton('afterend'); } /** * Builds the HTMX response. * Loading @@ -43,13 +63,23 @@ public function replace(): array { return $build; } /** * We need a static callback that ignores callback parameters. * * @return array * The render array. */ public static function replaceWithAjax(): array { return static::generateHtmxButton(); } /** * Static helper to for reusable render array. * * @return array * The render array. */ public static function generateHtmxButton(): array { public static function generateHtmxButton(string $swap = ''): array { $url = Url::fromRoute('test_htmx.attachments.replace'); $build['replace'] = [ '#type' => 'html_tag', Loading @@ -68,6 +98,9 @@ public static function generateHtmxButton(): array { ], ], ]; if ($swap !== '') { $build['replace']['#attributes']['data-hx-swap'] = $swap; } $build['content'] = [ '#type' => 'container', Loading
core/modules/system/tests/modules/test_htmx/src/Form/HtmxTestAjaxForm.php +1 −1 Original line number Diff line number Diff line Loading @@ -32,7 +32,7 @@ public function buildForm(array $form, FormStateInterface $form_state): array { '#ajax' => [ 'callback' => [ HtmxTestAttachmentsController::class, 'generateHtmxButton', 'replaceWithAjax', ], 'wrapper' => 'ajax-test-container', ], Loading
core/modules/system/tests/modules/test_htmx/test_htmx.routing.yml +16 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,22 @@ test_htmx.attachments.page: requirements: _permission: 'access content' test_htmx.attachments.before: path: '/htmx-test-attachments/before' defaults: _title: 'Page' _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::before' requirements: _permission: 'access content' test_htmx.attachments.after: path: '/htmx-test-attachments/after' defaults: _title: 'Page' _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::after' requirements: _permission: 'access content' test_htmx.attachments.replace: path: '/htmx-test-attachments/replace' defaults: Loading
core/tests/Drupal/Nightwatch/Tests/htmx/htmxTest.js +50 −4 Original line number Diff line number Diff line Loading @@ -41,8 +41,54 @@ module.exports = { .assert.not.elementPresent(cssSelector) .waitForElementVisible('[name="replace"]', 1000) .click('[name="replace"]') .waitForElementVisible(elementSelector, 6000) .waitForElementVisible(elementInitSelector, 6000) .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, 'Swap Before': (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 button and verify the absence // of the markup to be inserted. Click the button // and check for inserted javascript and markup. browser .drupalRelativeURL('/htmx-test-attachments/before') .waitForElementVisible('body', 1000) .assert.not.elementPresent(scriptSelector) .assert.not.elementPresent(cssSelector) .waitForElementVisible('[name="replace"]', 1000) .click('[name="replace"]') .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, 'Swap After': (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 button and verify the absence // of the markup to be inserted. Click the button // and check for inserted javascript and markup. browser .drupalRelativeURL('/htmx-test-attachments/after') .waitForElementVisible('body', 1000) .assert.not.elementPresent(scriptSelector) .assert.not.elementPresent(cssSelector) .waitForElementVisible('[name="replace"]', 1000) .click('[name="replace"]') .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, Loading @@ -69,8 +115,8 @@ module.exports = { .waitForElementVisible('[name="replace"]', 1000) .pause(1000) .click('[name="replace"]') .waitForElementVisible(elementSelector, 6000) .waitForElementVisible(elementInitSelector, 6000) .waitForElementVisible(elementSelector, 1100) .waitForElementVisible(elementInitSelector, 1100) .assert.elementPresent(scriptSelector) .assert.elementPresent(cssSelector); }, Loading