I am just reposting https://www.drupal.org/project/drupal/issues/2940212#comment-12721220 as a separate issue as requested.

Essentially, due to certain configurations of Views blocks, there are instances where the block may not have a viable preview that Layout Builder can use in the UI. This can cause confusing and difficulty placing and arranging blocks in the Layout Builder.

For instance, if you have a View with a contextual filter that does not validate in Layout Builder ( i.e. using a Node ID ) and the View is configured to "Hide View" if the filter present, when the block is placed in Layout Builder, there is no way to tell there is a block:

layout builder hidden views block example

I had a similar case where there are 3 views blocks with the same filter. When placing the block in Layout Builder, there are 3 blank places, making it impossible to know which block I am moving / configuring.

My suggestion is to add some sort of indicator that there is a block placed, even if the current display is hidden.

A couple of options that come to mind:

  • An outline around each placed block
  • A small label for the block name in a corner of the block ( maybe just when hovering or moving the block? )
  • Alternatively, use the View / Block ID as the label.

Here is potentially what a hidden block might look like:

proposed fix

Comments

scottsawyer created an issue. See original summary.

tim.plunkett’s picture

Title: Layout Builder UI - Views Blocks visibility / usability » Provide placeholders for empty blocks (for example, an empty Views listing)
Version: 8.5.6 » 8.7.x-dev
Issue tags: +Blocks-Layouts, +sprint
StatusFileSize
new5.58 KB

Perhaps we can solve this problem for all blocks at once.

tim.plunkett’s picture

--- a/core/lib/Drupal/Core/Block/BlockBase.php
+++ b/core/lib/Drupal/Core/Block/BlockBase.php

@@ -252,6 +252,13 @@ public function getMachineNameSuggestion() {
+  public function getPlaceholder() {

--- a/core/lib/Drupal/Core/Block/BlockPluginInterface.php
+++ b/core/lib/Drupal/Core/Block/BlockPluginInterface.php

@@ -153,4 +153,12 @@ public function blockSubmit($form, FormStateInterface $form_state);
+  public function getPlaceholder();

Because BlockBase and BlockPluginInterface have a tight 1:1 coupling, this is not considered a BC break.

scottsawyer’s picture

Brilliant, will test in about 5 hours.

tim.plunkett’s picture

Status: Active » Needs review
scottsawyer’s picture

Hey Tim,

I am using Drupal core 8.5.6, so I was unable to install the patch. Unfortunately, it's going to be a little while until I can test on 8.7. Maybe a few days.

Scott

tim.plunkett’s picture

StatusFileSize
new4.28 KB

Here's an 8.5.x compatible one for testing.
The 8.7.x patch will also work on 8.6.x

The last submitted patch, 2: 2992410-placeholder-2.patch, failed testing. View results

tim.plunkett’s picture

Need to switch the way it checks for emptiness... by using the one named isEmpty!

Status: Needs review » Needs work

The last submitted patch, 9: 2992410-placeholder-9.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new5.43 KB
new7.72 KB

Fixed a test, and renamed the method to be more clear.

johnwebdev’s picture

+++ b/core/modules/layout_builder/src/Plugin/Block/ExtraFieldBlock.php
@@ -130,13 +130,22 @@ public function build() {
+    $extra_fields = $this->entityFieldManager->getExtraFields($entity->getEntityTypeId(), $entity->bundle());

Hmm, I wish we had something to retrieve a single extra field instead, but its static cached anyhow so its fine.

I think this looks great, but still needs test coverage.

scottsawyer’s picture

I applied the patch in #7, but I am not seeing any difference on the manage display screen.

brian-c’s picture

This is working great for me with a Views block.

sugaroverflow’s picture

Issue tags: +Needs tests
StatusFileSize
new54.08 KB

Confirming that this works great!

Here's what I did:

1 - Created a related articles view block
2 - Added a Content: Has taxonomy term filter
3 - Added a contextual filter: Content: ID with the following settings: When the filter is not available- provide default value: "Content ID from URL"
4 - Attempted to place this block on the article content type, nothing appeared.
5 - Applied the patch from #11, cleared cache
6 - Observed a placeholder for my view -- attaching a screenshot.

Also needs tests.

tim.plunkett’s picture

Issue tags: -Needs tests
StatusFileSize
new2.35 KB
new10.07 KB

@sugaroverflow and I paired on this test.

The last submitted patch, 16: 2992410-placeholder-16-FAIL.patch, failed testing. View results

tim.plunkett’s picture

Issue tags: +MWDS2018
phenaproxima’s picture

I think this patch is a really, really nice idea. From a UX perspective, it's super-valuable (IMHO) to be able to show people where something will go.

+++ b/core/lib/Drupal/Core/Block/BlockPluginInterface.php
@@ -153,4 +153,15 @@ public function blockSubmit($form, FormStateInterface $form_state);
+  /**
+   * Returns a string to be used as a placeholder.
+   *
+   * This is typically used when the block has no output and must be displayed,
+   * for example during block configuration.
+   *
+   * @return string|\Drupal\Core\StringTranslation\TranslatableMarkup
+   *   A placeholder string for this block.
+   */
+  public function getPlaceholderString();

I wonder if this should be its own interface. It's entirely possible that this functionality might be useful for things that are not blocks, so maybe we could just have a stand-alone \Drupal\Core\Render\PlaceholderInterface and get BlockPluginInterface to extend it. What do you think?

Additionally, I wonder if this should not return a renderable array. That would open the door to using things like placeholder images or other neatness. On the other hand, I'm not sure if it would complicate things too much. Worth mentioning, though!

EDIT: It looks like the core render system already has some code in place for dealing with placeholders -- I believe it is there because of BigPipe. Could be worth looking into that existing code?

tim.plunkett’s picture

I'm fine with a separate interface, that could indeed be useful. I also removed the API addition to BlockPluginInterface and added an instanceof check before calling the method. But I still put it on BlockBase.


+++ b/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php
@@ -98,6 +99,9 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) {
+        $build['content']['#markup'] = $block->getPlaceholderString();

If we switched from a string to a render array, we'd have to make this $build['content']['placeholder'] = $block->getPlaceholder(); to avoid conflicts with any existing #cache keys.

And then all existing implementations will have return ['#markup' => $whatever]; instead of return $whatever;

Not ruling it out, just thinking it through...
It's probably the right thing to do to be future-thinking, but it certainly makes things unwieldy until we need more than a string.

phenaproxima’s picture

  1. +++ b/core/lib/Drupal/Core/Block/BlockBase.php
    @@ -252,6 +253,13 @@ public function getMachineNameSuggestion() {
    +    return $this->t('Placeholder for the "@block" block', ['@block' => $this->getPluginDefinition()['admin_label']]);
    

    Wouldn't we want to use $this->label() here? It will default to admin_label if not set, and I feel like that might be clearer for the users, if they're setting the label in a meaningful way.

  2. +++ b/core/modules/layout_builder/tests/src/Kernel/FieldBlockTest.php
    @@ -287,7 +287,7 @@ public function providerTestBuild() {
    -      'Placeholder for the "The Field Label" field',
    +      '',
         ];
         $data['exception, no preview'] = [
           new ThrowPromise(new \Exception('The exception message')),
    @@ -299,7 +299,7 @@ public function providerTestBuild() {
    
    @@ -299,7 +299,7 @@ public function providerTestBuild() {
         $data['exception, preview'] = [
           new ThrowPromise(new \Exception('The exception message')),
           TRUE,
    -      'Placeholder for the "The Field Label" field',
    +      '',
    

    Why was this changed? Not objecting, just asking in case committers do...

tim.plunkett’s picture

1) I thought about this and decided against it at first, but on second thought I agree

2) Previously FieldBlock had it's own built in placeholder system based on the existence of $entity->in_preview. Now that we're removing it, these tests should be streamlined completely.

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

Okay, I feel pretty good about it. Let's make it happen!

  • larowlan committed 53f1980 on 8.7.x
    Issue #2992410 by tim.plunkett, scottsawyer, sugaroverflow, phenaproxima...
larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Committed 53f1980 and pushed to 8.7.x.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

wim leers’s picture

Status: Closed (fixed) » Active

@tim.plunkett mentioned #3007439: Layout builder renders Book navigation block on non-book pages was a Blocks-Layouts blocker. So I looked at it … and discovered a unknown-to-me PlaceholderInterface, which was introduced here. Quoting my comment at #3007439-10: Layout builder renders Book navigation block on non-book pages.1:

  1. +++ b/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php
    @@ -90,6 +90,14 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) {
    +      $is_placeholder_ready = $event->inPreview() && $block instanceof PlaceholderInterface;
    ...
    +
    
    @@ -98,9 +106,9 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) {
             '#weight' => $event->getComponent()->getWeight(),
    

    TIL \Drupal\Core\Render\PlaceholderInterface is a thing! :O

    Introduced in #2992410: Provide placeholders for empty blocks (for example, an empty Views listing) apparently.

    This "placeholder" concept does not match the one in \Drupal\Core\Render\Placeholder\PlaceholderStrategyInterface … that's … very confusing :( I'm very sorry to raise this only now, but I hadn't seen that issue yet, and there was no CR for it. I'll leave a comment there.

So yeah … I really really hate to reopen this, but I think I have to, to avoid future confusion.

Pre-existing:

  • \Drupal\Core\Render\PlaceholderGenerator(Interface)
  • \Drupal\Core\Render\PlaceholderingRenderCache
  • \Drupal\Core\Render\Placeholder\PlaceholderStrategyInterface
  • \Drupal\Core\Render\Placeholder\ChainedPlaceholderStrategy
  • \Drupal\Core\Render\Placeholder\SingleFlushStrategy
  • \Drupal\big_pipe\Render\Placeholder\BigPipeStrategy

Added in this issue:

  • \Drupal\Core\Render\PlaceholderInterface

The pre-existing code/classes/interfaces ones define "placeholder" as an something to be replaced with final content. Used to for example be able to cache the entire /node/42 response except for the comment form which is then cacheable by Dynamic Page Cache; the comment form is present only as a placeholder, and is replaced by \Drupal\Core\Render\Renderer::replacePlaceholders() just before sending a response to the end user. A placeholder looks like this: <drupal-render-placeholder callback="…" arguments="…" token="…" ></drupal-render-placeholder — the end user never gets to see this. That's the SingleFlushStrategy. When BigPipe is installed, the placeholders look different: they become BigPipe placeholders (currently just empty <div>s since they do end up in the end user's DOM, but #2632750: Interface previews/skeleton screens through optional "preview" or "placeholder" templates would change this so that a more meaningful placeholder can be shown: an interface preview, or alternatively, perhaps just a spinner).

The interface added in this issue defines "placeholder" to be something rather different, for a different purpose. It's even explicitly for blocks that commonly render to something that's empty! If that weren't the case, then perhaps it could be used to help solve #2632750: Interface previews/skeleton screens through optional "preview" or "placeholder" templates. But because it's explicitly about "placeholder to make something that may not show up still be visually present, accessible and navigatable", it seems fundamentally different?

Hence my concern here: we now have two very different "placeholder" concepts in the \Drupal\Core\Render namespace.

This was only committed to Drupal 8.7. I think we can rename the interface introduced here without breaking BC.

Sorry Tim! :(

dsnopek’s picture

As far as terminology: maybe this could be a "block preview" rather than a "placeholder"?

wim leers’s picture

#28: yep, exactly.

And the more I think about it, the more I think that this needs exactly the same infrastructure as #2632750: Interface previews/skeleton screens through optional "preview" or "placeholder" templates. I was thinking about that issue again today and had forgotten I'd already made this connection a week ago in #7

It'd be wonderful if we could introduce one "preview interface" and use it for both Layout Builder and BigPipe!

tim.plunkett’s picture

We can do s/getPlaceholderString/getPreviewFallbackString
As that's what it is, a fallback when the preview fails

wim leers’s picture

As that's what it is, a fallback when the preview fails

Makes sense 👍

I think it then also makes sense to rename PlaceholderInterface to PreviewFallbackInterface. What do you think?

  • webchick committed c1b4712 on 8.6.x authored by larowlan
    Issue #2992410 by tim.plunkett, scottsawyer, sugaroverflow, phenaproxima...
webchick’s picture

Also backported to 8.6.x.

tim.plunkett’s picture

With two commits here, unless we plan to fully revert this then let's close and move to a follow-up.
The backport puts a deadline on that for the next 8.6 minor...

#3013187: Rename PlaceholderInterface to PreviewFallbackInterface

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.