Commit 9b768d70 authored by webchick's avatar webchick

Issue #2443457 by Wim Leers, dawehner, Berdir, giancarlosotelo, Jaesin: Views...

Issue #2443457 by Wim Leers, dawehner, Berdir, giancarlosotelo, Jaesin: Views block displayed when no results returned
parent 445b4cd1
...@@ -63,6 +63,11 @@ public function access(AccountInterface $account, $return_as_object = FALSE); ...@@ -63,6 +63,11 @@ public function access(AccountInterface $account, $return_as_object = FALSE);
/** /**
* Builds and returns the renderable array for this block plugin. * Builds and returns the renderable array for this block plugin.
* *
* If a block should not be rendered because it has no content, then this
* method must also ensure to return no content: it must then only return an
* empty array, or an empty array with #cache set (with cacheability metadata
* indicating the circumstances for it being empty).
*
* @return array * @return array
* A renderable array representing the content of the block. * A renderable array representing the content of the block.
* *
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
namespace Drupal\block\Tests\Views; namespace Drupal\block\Tests\Views;
use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Json;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\views\Entity\View;
use Drupal\views\Views; use Drupal\views\Views;
use Drupal\views\Tests\ViewTestBase; use Drupal\views\Tests\ViewTestBase;
use Drupal\views\Tests\ViewTestData; use Drupal\views\Tests\ViewTestData;
...@@ -21,6 +23,8 @@ ...@@ -21,6 +23,8 @@
*/ */
class DisplayBlockTest extends ViewTestBase { class DisplayBlockTest extends ViewTestBase {
use AssertPageCacheContextsAndTagsTrait;
/** /**
* Modules to install. * Modules to install.
* *
...@@ -259,6 +263,87 @@ public function testBlockRendering() { ...@@ -259,6 +263,87 @@ public function testBlockRendering() {
$this->drupalGet(''); $this->drupalGet('');
$result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2'); $result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2');
$this->assertTrue(empty($result), 'The title is not visible.'); $this->assertTrue(empty($result), 'The title is not visible.');
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:system.site', 'config:views.view.test_view_block' ,'rendered']));
}
/**
* Tests the various testcases of empty block rendering.
*/
public function testBlockEmptyRendering() {
// Remove all views_test_data entries.
\Drupal::database()->truncate('views_test_data')->execute();
/** @var \Drupal\views\ViewEntityInterface $view */
$view = View::load('test_view_block');
$view->invalidateCaches();
$block = $this->drupalPlaceBlock('views_block:test_view_block-block_1', array('label' => 'test_view_block-block_1:1', 'views_label' => 'Custom title'));
$this->drupalGet('');
$this->assertEqual(1, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
$display = &$view->getDisplay('block_1');
$display['display_options']['block_hide_empty'] = TRUE;
$view->save();
$this->drupalGet('');
$this->assertEqual(0, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
// Ensure that the view cachability metadata is propagated even, for an
// empty block.
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:system.site', 'config:views.view.test_view_block' ,'rendered']));
$this->assertCacheContexts(['url.query_args:_wrapper_format', 'user.roles:authenticated']);
// Add a header displayed on empty result.
$display = &$view->getDisplay('block_1');
$display['display_options']['defaults']['header'] = FALSE;
$display['display_options']['header']['example'] = [
'field' => 'area_text_custom',
'id' => 'area_text_custom',
'table' => 'views',
'plugin_id' => 'text_custom',
'content' => 'test header',
'empty' => TRUE,
];
$view->save();
$this->drupalGet('');
$this->assertEqual(1, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:system.site', 'config:views.view.test_view_block' ,'rendered']));
$this->assertCacheContexts(['url.query_args:_wrapper_format', 'user.roles:authenticated']);
// Hide the header on empty results.
$display = &$view->getDisplay('block_1');
$display['display_options']['defaults']['header'] = FALSE;
$display['display_options']['header']['example'] = [
'field' => 'area_text_custom',
'id' => 'area_text_custom',
'table' => 'views',
'plugin_id' => 'text_custom',
'content' => 'test header',
'empty' => FALSE,
];
$view->save();
$this->drupalGet('');
$this->assertEqual(0, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:system.site', 'config:views.view.test_view_block' ,'rendered']));
$this->assertCacheContexts(['url.query_args:_wrapper_format', 'user.roles:authenticated']);
// Add an empty text.
$display = &$view->getDisplay('block_1');
$display['display_options']['defaults']['empty'] = FALSE;
$display['display_options']['empty']['example'] = [
'field' => 'area_text_custom',
'id' => 'area_text_custom',
'table' => 'views',
'plugin_id' => 'text_custom',
'content' => 'test empty',
];
$view->save();
$this->drupalGet('');
$this->assertEqual(1, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:system.site', 'config:views.view.test_view_block' ,'rendered']));
$this->assertCacheContexts(['url.query_args:_wrapper_format', 'user.roles:authenticated']);
} }
/** /**
......
...@@ -38,6 +38,12 @@ public function getInfo() { ...@@ -38,6 +38,12 @@ public function getInfo() {
* View element pre render callback. * View element pre render callback.
*/ */
public static function preRenderViewElement($element) { public static function preRenderViewElement($element) {
// Allow specific Views displays to explicitly perform pre-rendering, for
// those displays that need to be able to know the fully built render array.
if (!empty($element['#pre_rendered'])) {
return $element;
}
if (!isset($element['#view'])) { if (!isset($element['#view'])) {
$view = Views::getView($element['#name']); $view = Views::getView($element['#name']);
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
use Drupal\Component\Utility\Xss; use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\Entity\Query\Query; use Drupal\Core\Config\Entity\Query\Query;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Element\View;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
...@@ -38,6 +39,20 @@ public function build() { ...@@ -38,6 +39,20 @@ public function build() {
// Before returning the block output, convert it to a renderable array // Before returning the block output, convert it to a renderable array
// with contextual links. // with contextual links.
$this->addContextualLinks($output); $this->addContextualLinks($output);
// Block module expects to get a final render array, without another
// top-level #pre_render callback. So, here we make sure that Views'
// #pre_render callback has already been applied.
$output = View::preRenderViewElement($output);
// When view_build is empty, the actual render array output for this View
// is going to be empty. In that case, return just #cache, so that the
// render system knows the reasons (cache contexts & tags) why this Views
// block is empty, and can cache it accordingly.
if (empty($output['view_build'])) {
$output = ['#cache' => $output['#cache']];
}
return $output; return $output;
} }
......
...@@ -141,7 +141,7 @@ protected function setUp() { ...@@ -141,7 +141,7 @@ protected function setUp() {
*/ */
public function testBuild() { public function testBuild() {
$output = $this->randomMachineName(100); $output = $this->randomMachineName(100);
$build = array('#markup' => $output, '#view_id' => 'test_view', '#view_display_plugin_class' => '\Drupal\views\Plugin\views\display\Block', '#view_display_show_admin_links' => FALSE, '#view_display_plugin_id' => 'block'); $build = array('view_build' => $output, '#view_id' => 'test_view', '#view_display_plugin_class' => '\Drupal\views\Plugin\views\display\Block', '#view_display_show_admin_links' => FALSE, '#view_display_plugin_id' => 'block', '#pre_rendered' => TRUE);
$this->executable->expects($this->once()) $this->executable->expects($this->once())
->method('buildRenderable') ->method('buildRenderable')
->with('block_1', []) ->with('block_1', [])
...@@ -157,6 +157,28 @@ public function testBuild() { ...@@ -157,6 +157,28 @@ public function testBuild() {
$this->assertEquals($build, $plugin->build()); $this->assertEquals($build, $plugin->build());
} }
/**
* Tests the build method.
*
* @covers ::build
*/
public function testBuildEmpty() {
$build = ['view_build' => [], '#view_id' => 'test_view', '#view_display_plugin_class' => '\Drupal\views\Plugin\views\display\Block', '#view_display_show_admin_links' => FALSE, '#view_display_plugin_id' => 'block', '#pre_rendered' => TRUE, '#cache' => ['contexts' => ['user']]];
$this->executable->expects($this->once())
->method('buildRenderable')
->with('block_1', [])
->willReturn($build);
$block_id = 'views_block:test_view-block_1';
$config = [];
$definition = [];
$definition['provider'] = 'views';
$plugin = new ViewsBlock($config, $block_id, $definition, $this->executableFactory, $this->storage, $this->account);
$this->assertEquals(array_intersect_key($build, ['#cache' => TRUE]), $plugin->build());
}
/** /**
* Tests the build method with a failed execution. * Tests the build method with a failed execution.
* *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment