Commit f85ec8e8 authored by catch's avatar catch
Browse files

Issue #3324062 by effulgentsia, nod_, longwave, alexpott, catch, lauriii,...

Issue #3324062 by effulgentsia, nod_, longwave, alexpott, catch, lauriii, andypost: [Regression] Changes to Drupal.ajax in 9.5.x have caused regressions in paragraphs_features module

(cherry picked from commit 6c5374eb)
parent 18e211cc
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -505,6 +505,9 @@
    ajax.options = {
      url: ajax.url,
      data: ajax.submit,
      isInProgress() {
        return ajax.ajaxing;
      },
      beforeSerialize(elementSettings, options) {
        return ajax.beforeSerialize(elementSettings, options);
      },
@@ -552,6 +555,17 @@
            // finished executing.
            .then(() => {
              ajax.ajaxing = false;
              // jQuery normally triggers the ajaxSuccess, ajaxComplete, and
              // ajaxStop events after the "success" function passed to $.ajax()
              // returns, but we prevented that via
              // $.event.special[EVENT_NAME].trigger in order to wait for the
              // commands to finish executing. Now that they have, re-trigger
              // those events.
              $(document).trigger('ajaxSuccess', [xmlhttprequest, this]);
              $(document).trigger('ajaxComplete', [xmlhttprequest, this]);
              if (--$.active === 0) {
                $(document).trigger('ajaxStop');
              }
            })
        );
      },
@@ -1735,4 +1749,50 @@
      });
    },
  };

  /**
   * Delay jQuery's global completion events until after commands have executed.
   *
   * jQuery triggers the ajaxSuccess, ajaxComplete, and ajaxStop events after
   * a successful response is returned and local success and complete events
   * are triggered. However, Drupal Ajax responses contain commands that run
   * asynchronously in a queue, so the following stops these events from getting
   * triggered until after the Promise that executes the command queue is
   * resolved.
   */
  const stopEvent = (xhr, settings) => {
    return (
      // Only interfere with Drupal's Ajax responses.
      xhr.getResponseHeader('X-Drupal-Ajax-Token') === '1' &&
      // The isInProgress() function might not be defined if the Ajax request
      // was initiated without Drupal.ajax() or new Drupal.Ajax().
      settings.isInProgress &&
      // Until this is false, the Ajax request isn't completely done (the
      // response's commands might still be running).
      settings.isInProgress()
    );
  };
  $.extend(true, $.event.special, {
    ajaxSuccess: {
      trigger(event, xhr, settings) {
        if (stopEvent(xhr, settings)) {
          return false;
        }
      },
    },
    ajaxComplete: {
      trigger(event, xhr, settings) {
        if (stopEvent(xhr, settings)) {
          // jQuery decrements its internal active ajax counter even when we
          // stop the ajaxComplete event, but we don't want that counter
          // decremented, because for our purposes this request is still active
          // while commands are executing. By incrementing it here, the net
          // effect is that it remains unchanged. By remaining above 0, the
          // ajaxStop event is also prevented.
          $.active++;
          return false;
        }
      },
    },
  });
})(jQuery, window, Drupal, drupalSettings, loadjs, window.tabbable);
+6 −0
Original line number Diff line number Diff line
@@ -42,3 +42,9 @@ command_promise:
    - core/jquery
    - core/drupal
    - core/drupal.ajax

global_events:
  js:
    js/global_events.js: {}
  dependencies:
    - core/drupal.ajax
+14 −0
Original line number Diff line number Diff line
@@ -101,3 +101,17 @@ ajax_test.promise:
    _form: '\Drupal\ajax_test\Form\AjaxTestFormPromise'
  requirements:
    _access: 'TRUE'

ajax_test.global_events:
  path: '/ajax-test/global-events'
  defaults:
    _controller: '\Drupal\ajax_test\Controller\AjaxTestController::globalEvents'
  requirements:
    _access: 'TRUE'

ajax_test.global_events_clear_log:
  path: '/ajax-test/global-events/clear-log'
  defaults:
    _controller: '\Drupal\ajax_test\Controller\AjaxTestController::globalEventsClearLog'
  requirements:
    _access: 'TRUE'
+14 −0
Original line number Diff line number Diff line
/**
 * @file
 * For testing that jQuery's ajaxSuccess, ajaxComplete, and ajaxStop events
 * are triggered only after commands in a Drupal Ajax response are executed.
 */

(($, Drupal) => {
  ['ajaxSuccess', 'ajaxComplete', 'ajaxStop'].forEach((eventName) => {
    $(document)[eventName](() => {
      $('#test_global_events_log').append(eventName);
      $('#test_global_events_log2').append(eventName);
    });
  });
})(jQuery, Drupal);
+33 −0
Original line number Diff line number Diff line
@@ -354,4 +354,37 @@ protected function getRenderTypes() {
    return $render_info;
  }

  /**
   * Returns a page from which to test Ajax global events.
   *
   * @return array
   *   The render array.
   */
  public function globalEvents() {
    return [
      '#attached' => [
        'library' => [
          'ajax_test/global_events',
        ],
      ],
      '#markup' => implode('', [
        '<div id="test_global_events_log"></div>',
        '<a id="test_global_events_drupal_ajax_link" class="use-ajax" href="' . Url::fromRoute('ajax_test.global_events_clear_log')->toString() . '">Drupal Ajax</a>',
        '<div id="test_global_events_log2"></div>',
      ]),
    ];
  }

  /**
   * Returns an AjaxResponse with command to clear the 'test_global_events_log'.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   The JSON response object.
   */
  public function globalEventsClearLog() {
    $response = new AjaxResponse();
    $response->addCommand(new HtmlCommand('#test_global_events_log', ''));
    return $response;
  }

}
Loading