diff --git a/facets.install b/facets.install index 82a9f6b..7678e81 100644 --- a/facets.install +++ b/facets.install @@ -7,6 +7,7 @@ use Drupal\facets\Entity\Facet; use Drupal\facets\Entity\FacetSource; +use Drupal\block\Entity\Block; /** * Convert facets on Search Api facet sources to use the display plugin. @@ -149,3 +150,20 @@ function facets_update_8005() { } } } + +/** + * Update facet blocks configuration with a block id used for AJAX support. + */ +function facets_update_8006() { + $query = \Drupal::entityQuery('block') + ->condition('plugin', 'facet_block', 'STARTS_WITH') + ->execute(); + + foreach ($query as $block_id) { + $block = Block::load($block_id); + $configuration = $block->get('settings'); + $configuration['block_id'] = $block_id; + $block->set('settings', $configuration); + $block->save(); + } +} diff --git a/facets.libraries.yml b/facets.libraries.yml index 2ebbf32..a77986d 100644 --- a/facets.libraries.yml +++ b/facets.libraries.yml @@ -53,3 +53,12 @@ soft-limit: - core/jquery.once - core/drupal - core/drupalSettings +drupal.facets.views-ajax: + js: + js/facets-views-ajax.js: {} + dependencies: + - core/jquery + - core/jquery.once + - core/drupal + - core/drupalSettings + - core/drupal.ajax diff --git a/facets.routing.yml b/facets.routing.yml index 1e897e7..9f26911 100644 --- a/facets.routing.yml +++ b/facets.routing.yml @@ -48,3 +48,10 @@ entity.facets_facet_source.edit_form: _title: 'Edit facet source configuration' requirements: _entity_create_access: 'facets_facet' + +facets.block.ajax: + path: '/facets-block-ajax' + defaults: + _controller: '\Drupal\facets\Controller\FacetBlockAjaxController::ajaxFacetBlockView' + requirements: + _access: 'TRUE' diff --git a/js/checkbox-widget.js b/js/checkbox-widget.js index 335d924..b6f00f7 100644 --- a/js/checkbox-widget.js +++ b/js/checkbox-widget.js @@ -43,7 +43,7 @@ checkbox.on('change.facets', function (e) { Drupal.facets.disableFacet($link.parents('.js-facets-checkbox-links')); - window.location.href = $(this).data('facetsredir'); + $(this).siblings('a')[0].click(); }); if (active) { @@ -51,7 +51,7 @@ label.find('.js-facet-deactivate').remove(); } - $link.before(checkbox).before(label).remove(); + $link.before(checkbox).before(label).hide(); }; diff --git a/js/dropdown-widget.js b/js/dropdown-widget.js index e87b929..1e790c5 100644 --- a/js/dropdown-widget.js +++ b/js/dropdown-widget.js @@ -38,6 +38,7 @@ $dropdown.removeClass('js-facets-dropdown-links'); $dropdown.addClass('facets-dropdown'); + $dropdown.addClass('js-facets-dropdown'); var id = $(this).data('drupal-facet-id'); var default_option_label = settings.facets.dropdown_widget[id]['facet-default-option-label']; @@ -68,7 +69,8 @@ // Go to the selected option when it's clicked. $dropdown.on('change.facets', function () { - window.location.href = $(this).val(); + var a = $($ul).find("[data-drupal-facet-item-id='" + $(this).find(':selected').data('drupalFacetItemId') + "']"); + $(a)[0].click(); }); // Append empty text option. @@ -77,7 +79,7 @@ } // Replace links with dropdown. - $ul.after($dropdown).remove(); + $ul.after($dropdown).hide(); Drupal.attachBehaviors($dropdown.parent()[0], Drupal.settings); }); }; diff --git a/js/facets-views-ajax.js b/js/facets-views-ajax.js new file mode 100644 index 0000000..3f2fac5 --- /dev/null +++ b/js/facets-views-ajax.js @@ -0,0 +1,203 @@ +/** + * @file + * Facets views AJAX handling. + */ + + +(function ($, Drupal) { + 'use strict'; + + /** + * Keep the original beforeSend method to use it later. + */ + var beforeSend = Drupal.Ajax.prototype.beforeSend; + + /** + * Trigger views AJAX refresh on click. + */ + Drupal.behaviors.facetsViewsAjax = { + attach: function (context, settings) { + + // Loop through all facets. + $.each(settings.facets_views_ajax, function (facetId, facetSettings) { + // Get the View for the current facet. + var view, current_dom_id, view_path; + if (settings.views && settings.views.ajaxViews) { + $.each(settings.views.ajaxViews, function (domId, viewSettings) { + // Check if we have facet for this view. + if (facetSettings.view_id == viewSettings.view_name && facetSettings.current_display_id == viewSettings.view_display_id) { + view = $('.js-view-dom-id-' + viewSettings.view_dom_id); + current_dom_id = viewSettings.view_dom_id; + view_path = facetSettings.ajax_path; + } + }); + } + + if (!view || view.length != 1) { + return; + } + + // Update view on summary block click. + if (updateFacetsSummaryBlock() && (facetId === 'facets_summary_ajax')) { + $('[data-drupal-facets-summary-id=' + facetSettings.facets_summary_id + ']').children('ul').children('li').once().click(function (e) { + e.preventDefault(); + var facetLink = $(this).find('a'); + updateFacetsView(facetLink.attr('href'), current_dom_id, view_path); + }); + } + // Update view on facet item click. + else { + $('[data-drupal-facet-id=' + facetId + ']').find('.facet-item').once().each(function (index, facet_item) { + $(facet_item).children('a').once().click(function (e) { + e.preventDefault(); + updateFacetsView($(this).attr('href'), current_dom_id, view_path); + }); + }); + + $('[data-drupal-facet-id=' + facetId + ']').each(function (index, facet_item) { + if ($(facet_item).hasClass('js-facets-dropdown')) { + $(facet_item).unbind('change.facets'); + $(facet_item).on('change.facets', function () { + updateFacetsView($(this).val(), current_dom_id, view_path); + }); + } + }); + + } + }); + } + }; + + // Helper function to update views output & Ajax facets. + var updateFacetsView = function (href, current_dom_id, view_path) { + // Refresh view. + var views_parameters = Drupal.Views.parseQueryString(href); + var views_arguments = Drupal.Views.parseViewArgs(href, 'search'); + var views_settings = $.extend( + {}, + Drupal.views.instances['views_dom_id:' + current_dom_id].settings, + views_arguments, + views_parameters + ); + + // Update View. + var views_ajax_settings = Drupal.views.instances['views_dom_id:' + current_dom_id].element_settings; + views_ajax_settings.submit = views_settings; + views_ajax_settings.url = view_path + '?q=' + href; + + Drupal.ajax(views_ajax_settings).execute(); + + // Update url. + window.historyInitiated = true; + window.history.pushState(null, document.title, href); + + // ToDo: Update views+facets with ajax on history back. + // For now we will reload the full page. + window.addEventListener("popstate", function (e) { + if (window.historyInitiated) { + window.location.reload(); + } + }); + + // Refresh facets blocks. + updateFacetsBlocks(href); + } + + // Helper function, updates facet blocks. + var updateFacetsBlocks = function (href) { + var settings = drupalSettings; + var facets_blocks = facetsBlocks(); + + // Update facet blocks. + let facet_settings = { + url: Drupal.url('facets-block-ajax'), + submit: { + facet_link: href, + facets_blocks: facets_blocks + } + }; + + // Update facets summary block. + if (updateFacetsSummaryBlock()) { + var facet_summary_wrapper_id = $('[data-drupal-facets-summary-id=' + settings.facets_views_ajax.facets_summary_ajax.facets_summary_id + ']').attr('id'); + var facet_summary_block_id = ''; + if (facet_summary_wrapper_id.indexOf('--') !== -1) { + facet_summary_block_id = facet_summary_wrapper_id.substring(0, facet_summary_wrapper_id.indexOf('--')).replace('block-', ''); + } + else { + facet_summary_block_id = facet_summary_wrapper_id.replace('block-', ''); + } + facet_settings.submit.update_summary_block = true; + facet_settings.submit.facet_summary_block_id = facet_summary_block_id; + facet_settings.submit.facet_summary_wrapper_id = settings.facets_views_ajax.facets_summary_ajax.facets_summary_id; + } + + Drupal.ajax(facet_settings).execute(); + }; + + // Helper function to determine if we should update the summary block. + // Returns true or false. + var updateFacetsSummaryBlock = function () { + var settings = drupalSettings; + var update_summary = false; + + if (settings.facets_views_ajax.facets_summary_ajax) { + update_summary = true; + } + + return update_summary; + }; + + // Helper function, return facet blocks. + var facetsBlocks = function () { + // Get all ajax facets blocks from the current page. + var facets_blocks = {}; + + $('.block-facets-ajax').each(function (index) { + var block_id_start = 'js-facet-block-id-'; + var block_id = $.map($(this).attr('class').split(' '), function (v, i) { + if (v.indexOf(block_id_start) > -1) { + return v.slice(block_id_start.length, v.length); + } + }).join(); + var block_selector = '#' + $(this).attr('id'); + facets_blocks[block_id] = block_selector; + }); + + return facets_blocks; + }; + + /** + * Overrides beforeSend to trigger facetblocks update on exposed filter change. + * + * @param {XMLHttpRequest} xmlhttprequest + * Native Ajax object. + * @param {object} options + * jQuery.ajax options. + */ + Drupal.Ajax.prototype.beforeSend = function (xmlhttprequest, options) { + + // Update facet blocks as well. + // Get view from options. + if (typeof options.extraData !== 'undefined' && typeof options.extraData.view_name !== 'undefined') { + var href = window.location.href; + var settings = drupalSettings; + + // TODO: Maybe we should limit facet block reloads by view? + var reload = false; + $.each(settings.facets_views_ajax, function (facetId, facetSettings) { + if (facetSettings.view_id == options.extraData.view_name && facetSettings.current_display_id == options.extraData.view_display_id) { + reload = true; + } + }); + + if (reload) { + updateFacetsBlocks(href); + } + } + + // Call the original Drupal method with the right context. + beforeSend.apply(this, arguments); + } + +})(jQuery, Drupal); diff --git a/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php b/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php index 93ccd99..83612e0 100644 --- a/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php +++ b/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php @@ -5,6 +5,7 @@ namespace Drupal\facets_summary\Plugin\Block; use Drupal\Core\Block\BlockBase; use Drupal\Core\Cache\UncacheableDependencyTrait; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Url; use Drupal\facets_summary\Entity\FacetsSummary; use Drupal\facets_summary\FacetsSummaryBlockInterface; use Drupal\facets_summary\FacetsSummaryManager\DefaultFacetsSummaryManager; @@ -96,6 +97,18 @@ class FacetsSummaryBlock extends BlockBase implements FacetsSummaryBlockInterfac ]; } + /** @var \Drupal\views\ViewExecutable $view */ + $view = $facets_summary->getFacetSource()->getViewsDisplay(); + + $build['#attached']['drupalSettings']['facets_views_ajax'] = [ + 'facets_summary_ajax' => [ + 'facets_summary_id' => $build['#attributes']['data-drupal-facets-summary-id'], + 'view_id' => $view->id(), + 'current_display_id' => $view->current_display, + 'ajax_path' => Url::fromRoute('views.ajax')->toString(), + ], + ]; + return $build; } diff --git a/src/Controller/FacetBlockAjaxController.php b/src/Controller/FacetBlockAjaxController.php new file mode 100644 index 0000000..72f1478 --- /dev/null +++ b/src/Controller/FacetBlockAjaxController.php @@ -0,0 +1,182 @@ +storage = $this->entityTypeManager()->getStorage('block'); + $this->renderer = $renderer; + $this->currentPath = $currentPath; + $this->router = $router; + $this->pathProcessor = $pathProcessor; + $this->currentRouteMatch = $currentRouteMatch; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('renderer'), + $container->get('path.current'), + $container->get('router'), + $container->get('path_processor_manager'), + $container->get('current_route_match') + ); + } + + /** + * Loads and renders the facet blocks via AJAX. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request object. + * + * @return \Drupal\Core\Ajax\AjaxResponse + * The ajax response. + * + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + * Thrown when the view was not found. + */ + public function ajaxFacetBlockView(Request $request) { + $response = new AjaxResponse(); + + // Rebuild the request and the current path, needed for facets. + $path = $request->request->get('facet_link'); + $facets_blocks = $request->request->get('facets_blocks'); + + // Make sure we are not updating blocks multiple times. + $facets_blocks = array_unique($facets_blocks); + + if (empty($path) || empty($facets_blocks)) { + throw new NotFoundHttpException('No facet link or facet blocks found.'); + } + + $this->currentRouteMatch->resetRouteMatch(); + $new_request = Request::create($path); + $request_stack = new RequestStack(); + $processed = $this->pathProcessor->processInbound($path, $new_request); + + $this->currentPath->setPath($processed, $new_request); + $request->attributes->add($this->router->matchRequest($new_request)); + $request_stack->push($new_request); + + $container = \Drupal::getContainer(); + $container->set('request_stack', $request_stack); + $active_facet = $request->request->get('active_facet'); + + // Build the facets blocks found for the current request and update. + foreach ($facets_blocks as $block_id => $block_selector) { + $block_entity = $this->storage->load($block_id); + + if ($block_entity) { + $block_view = $this + ->entityTypeManager + ->getViewBuilder('block') + ->view($block_entity); + + $block_view = (string) $this->renderer->renderPlain($block_view); + + // Make sure we retain the empty wrapper in case of empty facets to fill + // them up in next request if they have data. + $response->addCommand(new ReplaceCommand($block_selector, $block_view)); + } + } + + $response->addCommand(new InvokeCommand('[data-block-plugin-id="' . $active_facet . '"]', 'addClass', ['facet-active'])); + + $update_summary_block = $request->request->get('update_summary_block'); + + if ($update_summary_block) { + $facet_summary_block_id = $request->request->get('facet_summary_block_id'); + $facet_summary_wrapper_id = $request->request->get('facet_summary_wrapper_id'); + $facet_summary_block_id = str_replace('-', '_', $facet_summary_block_id); + + // Update filter summary block. + if ($facet_summary_block_id) { + $block_entity = $this->storage->load($facet_summary_block_id); + $block_view = $this->entityTypeManager->getViewBuilder('block') + ->view($block_entity); + /** @var \Drupal\Core\Render\Markup $block_view */ + $block_view = (string) $this->renderer->renderPlain($block_view); + + $response->addCommand(new ReplaceCommand('[data-drupal-facets-summary-id=' . $facet_summary_wrapper_id . ']', $block_view)); + } + } + + return $response; + } + +} diff --git a/src/FacetManager/DefaultFacetManager.php b/src/FacetManager/DefaultFacetManager.php index 7e33a30..4ccacce 100644 --- a/src/FacetManager/DefaultFacetManager.php +++ b/src/FacetManager/DefaultFacetManager.php @@ -334,7 +334,19 @@ class DefaultFacetManager { ]; } else { - return []; + // If the facet has no results, but it is being rendered trough ajax we + // should render a container (that is empty). This is because the + // javascript needs to be able to find a div to replace with the new + // content. + return [ + [ + '#type' => 'container', + '#attributes' => [ + 'data-drupal-facet-id' => $facet->id(), + 'class' => 'facet-empty', + ], + ], + ]; } } diff --git a/src/FacetSource/FacetSourcePluginBase.php b/src/FacetSource/FacetSourcePluginBase.php index 14317fb..9a470f8 100644 --- a/src/FacetSource/FacetSourcePluginBase.php +++ b/src/FacetSource/FacetSourcePluginBase.php @@ -116,6 +116,13 @@ abstract class FacetSourcePluginBase extends PluginBase implements FacetSourcePl return $this->keys; } + /** + * {@inheritdoc} + */ + public function buildFacet() { + return []; + } + /** * {@inheritdoc} */ diff --git a/src/FacetSource/FacetSourcePluginInterface.php b/src/FacetSource/FacetSourcePluginInterface.php index d88d3e5..1b97101 100644 --- a/src/FacetSource/FacetSourcePluginInterface.php +++ b/src/FacetSource/FacetSourcePluginInterface.php @@ -105,4 +105,14 @@ interface FacetSourcePluginInterface extends PluginFormInterface, DependentPlugi */ public function getDataDefinition($field_name); + /** + * Builds and returns an extra renderable array for this facet block plugin. + * + * @return array + * A renderable array representing the content of the block. + * + * @see \Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay + */ + public function buildFacet(); + } diff --git a/src/Plugin/Block/FacetBlock.php b/src/Plugin/Block/FacetBlock.php index a680387..ac224cc 100644 --- a/src/Plugin/Block/FacetBlock.php +++ b/src/Plugin/Block/FacetBlock.php @@ -5,6 +5,7 @@ namespace Drupal\facets\Plugin\Block; use Drupal\Core\Block\BlockBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginBase; use Drupal\facets\FacetManager\DefaultFacetManager; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -81,11 +82,22 @@ class FacetBlock extends BlockBase implements ContainerFactoryPluginInterface { // Let the facet_manager build the facets. $build = $this->facetManager->build($facet); - // Add contextual links only when we have results. if (!empty($build)) { + // Add extra elements from facet source, for example, ajax scripts. + // @see Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay + /* @var \Drupal\facets\FacetSource\FacetSourcePluginInterface $facet_source */ + $facet_source = $facet->getFacetSource(); + $build += $facet_source->buildFacet(); + + // Add contextual links only when we have results. $build['#contextual_links']['facets_facet'] = [ 'route_parameters' => ['facets_facet' => $facet->id()], ]; + + if (!empty($build['#use_ajax'])) { + $build['#attributes']['class'][] = 'block-facets-ajax'; + $build['#attributes']['class'][] = 'js-facet-block-id-' . $this->configuration['block_id']; + } } return $build; @@ -134,4 +146,14 @@ class FacetBlock extends BlockBase implements ContainerFactoryPluginInterface { return ['config' => [$facet->getConfigDependencyName()]]; } + /** + * {@inheritdoc} + */ + public function blockSubmit($form, FormStateInterface $form_state) { + // Save block id to configuration, we do this for loading the orignal block + // with ajax. + $block_id = $form['id']['#value']; + $this->configuration['block_id'] = $block_id; + } + } diff --git a/src/Plugin/facets/facet_source/SearchApiDisplay.php b/src/Plugin/facets/facet_source/SearchApiDisplay.php index 028d386..78d8bff 100644 --- a/src/Plugin/facets/facet_source/SearchApiDisplay.php +++ b/src/Plugin/facets/facet_source/SearchApiDisplay.php @@ -5,6 +5,7 @@ namespace Drupal\facets\Plugin\facets\facet_source; use Drupal\Component\Plugin\DependentPluginInterface; use Drupal\Core\Extension\ModuleHandler; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; use Drupal\facets\Exception\Exception; use Drupal\facets\Exception\InvalidQueryTypeException; use Drupal\facets\FacetInterface; @@ -372,4 +373,31 @@ class SearchApiDisplay extends FacetSourcePluginBase implements SearchApiFacetSo throw new Exception("Field with name {$field_name} does not have a definition"); } + /** + * {@inheritdoc} + */ + public function buildFacet() { + $build = parent::buildFacet(); + $view = $this->getViewsDisplay(); + if ($view === NULL) { + return $build; + } + + // Add JS for Views with Ajax Enabled. + if ($view->display_handler->ajaxEnabled()) { + $js_settings = [ + 'view_id' => $view->id(), + 'current_display_id' => $view->current_display, + 'view_base_path' => ltrim($view->getPath(), '/'), + 'ajax_path' => Url::fromRoute('views.ajax')->toString(), + ]; + $build['#attached']['library'][] = 'facets/drupal.facets.views-ajax'; + $build['#attached']['drupalSettings']['facets_views_ajax'] = [ + $this->facet->id() => $js_settings, + ]; + $build['#use_ajax'] = TRUE; + } + return $build; + } + } diff --git a/src/Widget/WidgetPluginBase.php b/src/Widget/WidgetPluginBase.php index 032cda8..30b4f63 100644 --- a/src/Widget/WidgetPluginBase.php +++ b/src/Widget/WidgetPluginBase.php @@ -6,6 +6,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Link; use Drupal\Core\Plugin\PluginBase; +use Drupal\Core\Url; use Drupal\facets\FacetInterface; use Drupal\facets\Result\Result; use Drupal\facets\Result\ResultInterface; @@ -48,6 +49,21 @@ abstract class WidgetPluginBase extends PluginBase implements WidgetPluginInterf return $this->buildResultItem($result); } else { + // When the facet is being build in an AJAX request, and the facetsource + // is a block, we need to update the url to use the current request url. + if ($result->getUrl()->isRouted() && $result->getUrl()->getRouteName() === 'facets.block.ajax') { + $request = \Drupal::request(); + $url_object = \Drupal::service('path.validator') + ->getUrlIfValid($request->getPathInfo()); + if ($url_object) { + $url = $result->getUrl(); + $options = $url->getOptions(); + $route_params = $url_object->getRouteParameters(); + $route_name = $url_object->getRouteName(); + $result->setUrl(new Url($route_name, $route_params, $options)); + } + } + return $this->buildListItems($facet, $result); } }, $facet->getResults()); diff --git a/tests/src/FunctionalJavascript/AjaxBehaviorTest.php b/tests/src/FunctionalJavascript/AjaxBehaviorTest.php new file mode 100644 index 0000000..a4c8d71 --- /dev/null +++ b/tests/src/FunctionalJavascript/AjaxBehaviorTest.php @@ -0,0 +1,74 @@ +createFacet('owl'); + $this->createFacet('duck', 'keywords'); + + // Go to the views page. + $this->drupalGet('search-api-test-fulltext'); + + // Make sure the block is shown on the page. + $page = $this->getSession()->getPage(); + $block_owl = $page->findById('block-owl-block'); + $block_owl->isVisible(); + $block_duck = $page->findById('block-duck-block'); + $block_duck->isVisible(); + } + + /** + * Create a facet. + * + * @param string $id + * The id of the facet. + * @param string $field + * The field name. + */ + protected function createFacet($id, $field = 'type') { + $facet_storage = \Drupal::entityTypeManager()->getStorage('facets_facet'); + // Create and save a facet with a checkbox widget. + $facet_storage->create([ + 'id' => $id, + 'name' => strtoupper($id), + 'url_alias' => $id, + 'facet_source_id' => 'search_api:views_page__search_api_test_view__page_1', + 'field_identifier' => $field, + 'empty_behavior' => ['behavior' => 'none'], + 'weight' => 1, + 'widget' => [ + 'type' => 'links', + 'config' => [ + 'show_numbers' => TRUE, + 'soft_limit' => 0, + ], + ], + 'processor_configs' => [ + 'url_processor_handler' => [ + 'processor_id' => 'url_processor_handler', + 'weights' => ['pre_query' => -10, 'build' => -10], + 'settings' => [], + ], + ], + ])->save(); + $this->createBlock($id); + } + +} diff --git a/tests/src/FunctionalJavascript/JsBase.php b/tests/src/FunctionalJavascript/JsBase.php new file mode 100644 index 0000000..28b9448 --- /dev/null +++ b/tests/src/FunctionalJavascript/JsBase.php @@ -0,0 +1,130 @@ +drupalCreateUser([ + 'administer search_api', + 'administer facets', + 'access administration pages', + 'administer blocks', + ]); + $this->drupalLogin($admin_user); + + $this->insertExampleContent(); + } + + /** + * Setup and insert test content. + */ + protected function insertExampleContent() { + entity_test_create_bundle('item', NULL, 'entity_test_mulrev_changed'); + entity_test_create_bundle('article', NULL, 'entity_test_mulrev_changed'); + + $entity_test_storage = \Drupal::entityTypeManager() + ->getStorage('entity_test_mulrev_changed'); + $entity_1 = $entity_test_storage->create([ + 'name' => 'foo bar baz', + 'body' => 'test test', + 'type' => 'item', + 'keywords' => ['orange'], + 'category' => 'item_category', + ]); + $entity_1->save(); + $entity_2 = $entity_test_storage->create([ + 'name' => 'foo test', + 'body' => 'bar test', + 'type' => 'item', + 'keywords' => ['orange', 'apple', 'grape'], + 'category' => 'item_category', + ]); + $entity_2->save(); + $entity_3 = $entity_test_storage->create([ + 'name' => 'bar', + 'body' => 'test foobar', + 'type' => 'item', + ]); + $entity_3->save(); + $entity_4 = $entity_test_storage->create([ + 'name' => 'foo baz', + 'body' => 'test test test', + 'type' => 'article', + 'keywords' => ['apple', 'strawberry', 'grape'], + 'category' => 'article_category', + ]); + $entity_4->save(); + $entity_5 = $entity_test_storage->create([ + 'name' => 'bar baz', + 'body' => 'foo', + 'type' => 'article', + 'keywords' => ['orange', 'strawberry', 'grape', 'banana'], + 'category' => 'article_category', + ]); + $entity_5->save(); + + $inserted_entities = \Drupal::entityQuery('entity_test_mulrev_changed') + ->count() + ->execute(); + $this->assertEquals(5, $inserted_entities, "5 items inserted."); + + /** @var \Drupal\search_api\IndexInterface $index */ + $index = Index::load('database_search_index'); + $indexed_items = $index->indexItems(); + $this->assertEquals(5, $indexed_items, '5 items indexed.'); + } + + /** + * Create and place a facet block in the first sidebar. + * + * @param string $id + * Create a block for a facet. + */ + protected function createBlock($id) { + $config = \Drupal::configFactory(); + $settings = [ + 'plugin' => 'facet_block:' . $id, + 'region' => 'sidebar_first', + 'id' => $id . '_block', + 'theme' => $config->get('system.theme')->get('default'), + 'label' => ucfirst($id) . ' block', + 'visibility' => [], + 'weight' => 0, + ]; + + foreach (['region', 'id', 'theme', 'plugin', 'weight', 'visibility'] as $key) { + $values[$key] = $settings[$key]; + // Remove extra values that do not belong in the settings array. + unset($settings[$key]); + } + $values['settings'] = $settings; + $block = Block::create($values); + $block->save(); + } + +} diff --git a/tests/src/FunctionalJavascript/WidgetJSTest.php b/tests/src/FunctionalJavascript/WidgetJSTest.php index 2b606a2..0d67d97 100644 --- a/tests/src/FunctionalJavascript/WidgetJSTest.php +++ b/tests/src/FunctionalJavascript/WidgetJSTest.php @@ -2,46 +2,14 @@ namespace Drupal\Tests\facets\FunctionalJavascript; -use Drupal\block\Entity\Block; use Drupal\facets\Entity\Facet; -use Drupal\FunctionalJavascriptTests\JavascriptTestBase; -use Drupal\search_api\Entity\Index; /** * Tests for the JS that transforms widgets into form elements. * * @group facets */ -class WidgetJSTest extends JavascriptTestBase { - - /** - * {@inheritdoc} - */ - public static $modules = [ - 'views', - 'search_api', - 'facets', - 'facets_search_api_dependency', - 'block', - ]; - - /** - * {@inheritdoc} - */ - public function setUp() { - parent::setUp(); - - // Create the users used for the tests. - $admin_user = $this->drupalCreateUser([ - 'administer search_api', - 'administer facets', - 'access administration pages', - 'administer blocks', - ]); - $this->drupalLogin($admin_user); - - $this->insertExampleContent(); - } +class WidgetJSTest extends JsBase { /** * Tests JS interactions in the admin UI. @@ -251,91 +219,4 @@ class WidgetJSTest extends JavascriptTestBase { $this->assertTrue(strpos($current_url, 'search-api-test-fulltext?f%5B0%5D=llama%253Aitem') !== FALSE); } - /** - * Setup and insert test content. - */ - protected function insertExampleContent() { - entity_test_create_bundle('item', NULL, 'entity_test_mulrev_changed'); - entity_test_create_bundle('article', NULL, 'entity_test_mulrev_changed'); - - $entity_test_storage = \Drupal::entityTypeManager() - ->getStorage('entity_test_mulrev_changed'); - $entity_1 = $entity_test_storage->create([ - 'name' => 'foo bar baz', - 'body' => 'test test', - 'type' => 'item', - 'keywords' => ['orange'], - 'category' => 'item_category', - ]); - $entity_1->save(); - $entity_2 = $entity_test_storage->create([ - 'name' => 'foo test', - 'body' => 'bar test', - 'type' => 'item', - 'keywords' => ['orange', 'apple', 'grape'], - 'category' => 'item_category', - ]); - $entity_2->save(); - $entity_3 = $entity_test_storage->create([ - 'name' => 'bar', - 'body' => 'test foobar', - 'type' => 'item', - ]); - $entity_3->save(); - $entity_4 = $entity_test_storage->create([ - 'name' => 'foo baz', - 'body' => 'test test test', - 'type' => 'article', - 'keywords' => ['apple', 'strawberry', 'grape'], - 'category' => 'article_category', - ]); - $entity_4->save(); - $entity_5 = $entity_test_storage->create([ - 'name' => 'bar baz', - 'body' => 'foo', - 'type' => 'article', - 'keywords' => ['orange', 'strawberry', 'grape', 'banana'], - 'category' => 'article_category', - ]); - $entity_5->save(); - - $inserted_entities = \Drupal::entityQuery('entity_test_mulrev_changed') - ->count() - ->execute(); - $this->assertEquals(5, $inserted_entities, "5 items inserted."); - - /** @var \Drupal\search_api\IndexInterface $index */ - $index = Index::load('database_search_index'); - $indexed_items = $index->indexItems(); - $this->assertEquals(5, $indexed_items, '5 items indexed.'); - } - - /** - * Create and place a facet block in the first sidebar. - * - * @param string $id - * Create a block for a facet. - */ - protected function createBlock($id) { - $config = \Drupal::configFactory(); - $settings = [ - 'plugin' => 'facet_block:' . $id, - 'region' => 'sidebar_first', - 'id' => $id . '_block', - 'theme' => $config->get('system.theme')->get('default'), - 'label' => ucfirst($id) . ' block', - 'visibility' => [], - 'weight' => 0, - ]; - - foreach (['region', 'id', 'theme', 'plugin', 'weight', 'visibility'] as $key) { - $values[$key] = $settings[$key]; - // Remove extra values that do not belong in the settings array. - unset($settings[$key]); - } - $values['settings'] = $settings; - $block = Block::create($values); - $block->save(); - } - }