diff --git a/core/modules/views/js/ajax_view.es6.js b/core/modules/views/js/ajax_view.es6.js index 0c8535890b5a005ae7f1f3da748ccc0e6b038d65..e4de7b3d3a2b988ba3787fcc80adc5b2d3f216df 100644 --- a/core/modules/views/js/ajax_view.es6.js +++ b/core/modules/views/js/ajax_view.es6.js @@ -13,14 +13,28 @@ * Attaches ajaxView functionality to relevant elements. */ Drupal.behaviors.ViewsAjaxView = {}; - Drupal.behaviors.ViewsAjaxView.attach = function () { - if (drupalSettings && drupalSettings.views && drupalSettings.views.ajaxViews) { - const ajaxViews = drupalSettings.views.ajaxViews; + Drupal.behaviors.ViewsAjaxView.attach = function (context, settings) { + if (settings && settings.views && settings.views.ajaxViews) { + const { views: { ajaxViews } } = settings; Object.keys(ajaxViews || {}).forEach((i) => { Drupal.views.instances[i] = new Drupal.views.ajaxView(ajaxViews[i]); }); } }; + Drupal.behaviors.ViewsAjaxView.detach = (context, settings, trigger) => { + if (trigger === 'unload') { + if (settings && settings.views && settings.views.ajaxViews) { + const { views: { ajaxViews } } = settings; + Object.keys(ajaxViews || {}).forEach((i) => { + const selector = `.js-view-dom-id-${ajaxViews[i].view_dom_id}`; + if ($(selector, context).length) { + delete Drupal.views.instances[i]; + delete settings.views.ajaxViews[i]; + } + }); + } + } + }; /** * @namespace diff --git a/core/modules/views/js/ajax_view.js b/core/modules/views/js/ajax_view.js index a10eb837eb9701425512aa126fb8f9272efaac88..95a803d7fec8ab22d42b8a87704f6a5f557318f2 100644 --- a/core/modules/views/js/ajax_view.js +++ b/core/modules/views/js/ajax_view.js @@ -7,14 +7,30 @@ (function ($, Drupal, drupalSettings) { Drupal.behaviors.ViewsAjaxView = {}; - Drupal.behaviors.ViewsAjaxView.attach = function () { - if (drupalSettings && drupalSettings.views && drupalSettings.views.ajaxViews) { - var ajaxViews = drupalSettings.views.ajaxViews; + Drupal.behaviors.ViewsAjaxView.attach = function (context, settings) { + if (settings && settings.views && settings.views.ajaxViews) { + var ajaxViews = settings.views.ajaxViews; + Object.keys(ajaxViews || {}).forEach(function (i) { Drupal.views.instances[i] = new Drupal.views.ajaxView(ajaxViews[i]); }); } }; + Drupal.behaviors.ViewsAjaxView.detach = function (context, settings, trigger) { + if (trigger === 'unload') { + if (settings && settings.views && settings.views.ajaxViews) { + var ajaxViews = settings.views.ajaxViews; + + Object.keys(ajaxViews || {}).forEach(function (i) { + var selector = '.js-view-dom-id-' + ajaxViews[i].view_dom_id; + if ($(selector, context).length) { + delete Drupal.views.instances[i]; + delete settings.views.ajaxViews[i]; + } + }); + } + } + }; Drupal.views = {}; diff --git a/core/modules/views/tests/modules/views_test_modal/src/Controller/TestController.php b/core/modules/views/tests/modules/views_test_modal/src/Controller/TestController.php new file mode 100644 index 0000000000000000000000000000000000000000..b89cf4081d737127cca793cfdd05de3d6926bc9d --- /dev/null +++ b/core/modules/views/tests/modules/views_test_modal/src/Controller/TestController.php @@ -0,0 +1,41 @@ + 'link', + '#title' => $this->t('Administer content'), + '#url' => Url::fromUserInput('/admin/content'), + '#attributes' => [ + 'class' => ['use-ajax'], + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'dialogClass' => 'views-test-modal', + 'height' => '50%', + 'width' => '50%', + 'title' => $this->t('Administer content'), + ]), + ], + '#attached' => [ + 'library' => [ + 'core/drupal.dialog.ajax', + ], + ], + ]; + + return $build; + } + +} diff --git a/core/modules/views/tests/modules/views_test_modal/views_test_modal.info.yml b/core/modules/views/tests/modules/views_test_modal/views_test_modal.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..59be6d4e51fb88a8d761e2042e8d8820614eee16 --- /dev/null +++ b/core/modules/views/tests/modules/views_test_modal/views_test_modal.info.yml @@ -0,0 +1,9 @@ +name: 'Views Test Modal' +type: module +description: 'Provides a test page that renders a View in a modal.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - drupal:node + - drupal:views diff --git a/core/modules/views/tests/modules/views_test_modal/views_test_modal.routing.yml b/core/modules/views/tests/modules/views_test_modal/views_test_modal.routing.yml new file mode 100644 index 0000000000000000000000000000000000000000..430bac18e523138bb9087514c3100de108bae972 --- /dev/null +++ b/core/modules/views/tests/modules/views_test_modal/views_test_modal.routing.yml @@ -0,0 +1,6 @@ +views_test_modal.modal: + path: '/views-test-modal/modal' + defaults: + _controller: '\Drupal\views_test_modal\Controller\TestController::modal' + requirements: + _access: 'TRUE' diff --git a/core/modules/views/tests/src/FunctionalJavascript/ExposedFilterAJAXTest.php b/core/modules/views/tests/src/FunctionalJavascript/ExposedFilterAJAXTest.php index 6aa52bcd12c1b4dd79a8dd00484ac5fade916120..c6cbaf5764125328de5e807b31ac581d536f1058 100644 --- a/core/modules/views/tests/src/FunctionalJavascript/ExposedFilterAJAXTest.php +++ b/core/modules/views/tests/src/FunctionalJavascript/ExposedFilterAJAXTest.php @@ -19,12 +19,14 @@ class ExposedFilterAJAXTest extends WebDriverTestBase { /** * {@inheritdoc} */ - public static $modules = ['node', 'views']; + public static $modules = ['node', 'views', 'views_test_modal']; /** - * Tests if exposed filtering via AJAX works for the "Content" View. + * {@inheritdoc} */ - public function testExposedFiltering() { + protected function setUp() { + parent::setUp(); + // Enable AJAX on the /admin/content View. \Drupal::configFactory()->getEditable('views.view.content') ->set('display.default.display_options.use_ajax', TRUE) @@ -43,7 +45,12 @@ public function testExposedFiltering() { 'edit any page content', ]); $this->drupalLogin($user); + } + /** + * Tests if exposed filtering via AJAX works for the "Content" View. + */ + public function testExposedFiltering() { // Visit the View page. $this->drupalGet('admin/content'); @@ -91,4 +98,52 @@ public function testExposedFiltering() { $this->assertFalse($session->getPage()->hasButton('Reset')); } + /** + * Tests if exposed filtering via AJAX works in a modal. + */ + public function testExposedFiltersInModal() { + $this->drupalGet('views-test-modal/modal'); + + $assert = $this->assertSession(); + + $assert->elementExists('named', ['link', 'Administer content'])->click(); + $dialog = $assert->waitForElementVisible('css', '.views-test-modal'); + + $session = $this->getSession(); + // Ensure that the Content we're testing for is present. + $html = $session->getPage()->getHtml(); + $this->assertContains('Page One', $html); + $this->assertContains('Page Two', $html); + + // Search for "Page One". + $session->getPage()->fillField('title', 'Page One'); + $assert->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Filter'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Verify that only the "Page One" Node is present. + $html = $session->getPage()->getHtml(); + $this->assertContains('Page One', $html); + $this->assertNotContains('Page Two', $html); + + // Close and re-open the modal. + $assert->buttonExists('Close', $dialog)->press(); + $assert->elementExists('named', ['link', 'Administer content'])->click(); + $assert->waitForElementVisible('css', '.views-test-modal'); + + // Ensure that the Content we're testing for is present. + $html = $session->getPage()->getHtml(); + $this->assertContains('Page One', $html); + $this->assertContains('Page Two', $html); + + // Search for "Page One". + $session->getPage()->fillField('title', 'Page One'); + $assert->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Filter'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Verify that only the "Page One" Node is present. + $html = $session->getPage()->getHtml(); + $this->assertContains('Page One', $html); + $this->assertNotContains('Page Two', $html); + } + }