Verified Commit 5edaa75e authored by Théodore Biadala's avatar Théodore Biadala
Browse files

Issue #3544486 by fathershawn, richgerdes, larowlan, smustgrave, nicxvan: Add...

Issue #3544486 by fathershawn, richgerdes, larowlan, smustgrave, nicxvan: Add methods to get info about which elements are involved in an HTMX Request
parent 3e9f2844
Loading
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Htmx\HtmxRequestInfoTrait;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\Core\Routing\RedirectDestinationTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -44,6 +45,7 @@
abstract class FormBase implements FormInterface, ContainerInjectionInterface {

  use DependencySerializationTrait;
  use HtmxRequestInfoTrait;
  use LoggerChannelTrait;
  use MessengerTrait;
  use RedirectDestinationTrait;
+102 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Core\Htmx;

/**
 * Provides methods for getting information about the HTMX request.
 */
trait HtmxRequestInfoTrait {

  /**
   * Gets the request object.
   *
   * @return \Symfony\Component\HttpFoundation\Request
   *   The request object.
   */
  abstract protected function getRequest();

  /**
   * Determines if the request is sent by HTMX.
   *
   * @return bool
   *   TRUE if the 'HX-Request' header is present.
   */
  protected function isHtmxRequest(): bool {
    return $this->getRequest()->headers->has('HX-Request');
  }

  /**
   * Determines if the request is boosted by HTMX.
   *
   * @return bool
   *   TRUE if the 'HX-Boosted' header is present.
   */
  protected function isHtmxBoosted(): bool {
    return $this->getRequest()->headers->has('HX-Boosted');
  }

  /**
   * Retrieves the URL of the requesting page from an HTMX request header.
   *
   * @return string
   *   The value of the 'HX-Current-URL' header, or an empty string if not set.
   */
  protected function getHtmxCurrentUrl(): string {
    return $this->getRequest()->headers->get('HX-Current-URL', '');
  }

  /**
   * Determines if if the request is for history restoration.
   *
   * Sent after a miss in the local history cache
   *
   * @return bool
   *   TRUE if the 'HX-History-Restore-Request' header is present.
   */
  protected function isHtmxHistoryRestoration(): bool {
    return $this->getRequest()->headers->has('HX-History-Restore-Request');
  }

  /**
   * Retrieves the prompt from an HTMX request header.
   *
   * @return string
   *   The value of the 'HX-Prompt' header, or an empty string if not set.
   */
  protected function getHtmxPrompt(): string {
    return $this->getRequest()->headers->get('HX-Prompt', '');
  }

  /**
   * Retrieves the target identifier from an HTMX request header.
   *
   * @return string
   *   The value of the 'HX-Target' header, or an empty string if not set.
   */
  protected function getHtmxTarget(): string {
    return $this->getRequest()->headers->get('HX-Target', '');
  }

  /**
   * Retrieves the trigger identifier from an HTMX request header.
   *
   * @return string
   *   The value of the 'HX-Trigger' header, or an empty string if not set.
   */
  protected function getHtmxTrigger(): string {
    return $this->getRequest()->headers->get('HX-Trigger', '');
  }

  /**
   * Retrieves the trigger name from an HTMX request header.
   *
   * @return string
   *   The value of the 'HX-Trigger-Name' header, or an empty string if not set.
   */
  protected function getHtmxTriggerName(): string {
    return $this->getRequest()->headers->get('HX-Trigger-Name', '');
  }

}
+138 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Tests\Core\Htmx;

use Drupal\Core\Htmx\HtmxRequestInfoTrait;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use Symfony\Component\HttpFoundation\Request;

/**
 * Test all HtmxRequestInfoTrait methods.
 */
#[CoversClass(HtmxRequestInfoTrait::class)]
#[Group('Htmx')]
class HtmxRequestInfoTest extends UnitTestCase {

  use HtmxRequestInfoTrait;

  /**
   * A simulated request.
   */
  protected Request $request;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->request = new Request();
  }

  /**
   * Tests the isHtmxRequest method.
   */
  public function testIsHtmxRequest(): void {
    // Test with the header not present.
    $this->assertFalse($this->isHtmxRequest());

    // Test with the header present.
    $this->request->headers->set('HX-Request', 'true');
    $this->assertTrue($this->isHtmxRequest());
  }

  /**
   * Tests the isHtmxBoosted method.
   */
  public function testIsHtmxBoosted(): void {
    // Test with the header not present.
    $this->assertFalse($this->isHtmxBoosted());

    // Test with the header present.
    $this->request->headers->set('HX-Boosted', 'true');
    $this->assertTrue($this->isHtmxBoosted());
  }

  /**
   * Tests the getHtmxCurrentUrl method.
   */
  public function testGetHtmxCurrentUrl(): void {
    // Test with the header not present.
    $this->assertEquals('', $this->getHtmxCurrentUrl());

    // Test with the header present.
    $this->request->headers->set('HX-Current-URL', 'https://example.com/page');
    $this->assertEquals('https://example.com/page', $this->getHtmxCurrentUrl());
  }

  /**
   * Tests the isHtmxHistoryRestoration method.
   */
  public function testIsHtmxHistoryRestoration(): void {
    // Test with the header not present.
    $this->assertFalse($this->isHtmxHistoryRestoration());

    // Test with the header present.
    $this->request->headers->set('HX-History-Restore-Request', 'true');
    $this->assertTrue($this->isHtmxHistoryRestoration());
  }

  /**
   * Tests the getHtmxPrompt method.
   */
  public function testGetHtmxPrompt(): void {
    // Test with the header not present.
    $this->assertEquals('', $this->getHtmxPrompt());

    // Test with the header present.
    $this->request->headers->set('HX-Prompt', 'Enter a value');
    $this->assertEquals('Enter a value', $this->getHtmxPrompt());
  }

  /**
   * Tests the getHtmxTarget method.
   */
  public function testGetHtmxTarget(): void {
    // Test with the header not present.
    $this->assertEquals('', $this->getHtmxTarget());

    // Test with the header present.
    $this->request->headers->set('HX-Target', 'submit-button');
    $this->assertEquals('submit-button', $this->getHtmxTarget());
  }

  /**
   * Tests the getHtmxTrigger method.
   */
  public function testGetHtmxTrigger(): void {
    // Test with the header not present.
    $this->assertEquals('', $this->getHtmxTrigger());

    // Test with the header present.
    $this->request->headers->set('HX-Trigger', 'submit-button');
    $this->assertEquals('submit-button', $this->getHtmxTrigger());
  }

  /**
   * Tests the getHtmxTriggerName method.
   */
  public function testGetHtmxTriggerName(): void {
    // Test with the header not present.
    $this->assertEquals('', $this->getHtmxTriggerName());

    // Test with the header present.
    $this->request->headers->set('HX-Trigger-Name', 'submit-button');
    $this->assertEquals('submit-button', $this->getHtmxTriggerName());
  }

  /**
   * {@inheritdoc}
   */
  protected function getRequest() {
    return $this->request;
  }

}