Drupal Live Site Build (Part 2) – Create Layout Builder Section, Bootstrap Carousel

Don’t forget to subscribe to our YouTube channel to stay up-to-date.

Video sections:

  • 01:39 Discuss bugs from last live stream
  • 09:37 Create custom module for layouts
  • 19:33 Review layout code
  • 22:04 Start creating custom layout
  • 25:37 Create layout plugin class
  • 28:00 Create twig template for layout
  • 31:46 Install custom module
  • 33:46 Delete inline block template (no longer needed)
  • 37:36 Discuss what needs to be implemented in new layout
  • 39:05 Implement custom code in new layout
  • 42:49 Debug build method in layout class
  • 47:53 Override the build method in layout class to add custom classes
  • 54:08 Review Bootstrap 4 grid system and necessary classes
  • 57:37 Create custom method to determine grid width
  • 1:03:04 Fix “Add block” button
  • 1:11:15 Discuss implementing carousel
  • 1:14:55 Discuss bug with Paragraphs module and block types
  • 1:17:43 Create Carousel block type
  • 1:22:12 Download Inline Entity Form
  • 1:23:14 Create Carousel item block type
  • 1:25:21 Create Carousel items field
  • 1:27:20 Configure the Inline Entity Form widget
  • 1:33:25 Start implementing Bootstrap carousel markup
  • 1:34:27 Create new carousel layout
  • 1:42:16 Customize Carousel item entity
  • 1:52:14 Implement preprocess function
  • 2:05:31 Implement markup changes in Twig
  • 2:16:49 Change image style size
  • 2:19:52 Create carousel Sass file
  • 2:23:30 Discuss next live streams

Series:

In the show notes below, I outline what we implement in part 2 of the Drupal live site build. We start off by fixing a few bugs, which I introduced and didn’t notice until I started playing around with the site after the first video. For example, we couldn’t drag-and-drop any of the card components because I overrode the inline-block template.

Part 2 (this video) is a little more advanced because we create a custom module, implement two layouts and implement a hook_preprocess_HOOK.

So we start off by creating a Row layout which fixes the bug from part 1 and we implement the Bootstrap Carousel component (clients love carousels) using block types.

💻 Get a copy of the built site from GitHub.

New Row Layout

To fix a few bugs created in the previous video, I decided we needed to create a custom layout. This layout would be called BS Row and it’ll be used as a section in layout builder.

Create Custom Module

Go ahead and create a custom module called ww_bootstrap4_layouts.

If you use Drush, just run the following command and fill in the prompts.

drush generate module

NOTE: The generate command only works in Drush 9 and up.

Or, you can do it the old fashion way.

1. Create a folder in modules/custom/ww_bootstrap_layouts.

2. Create a ww_bootstrap4_layouts.info.yml file and add the following into it:

name: WW Bootstrap4 layouts
type: module
description: Bootstrap4 layouts
package: WebWash
core: 8.x
core_version_requirement: ^8 || ^9

3. Create a ww_bootstrap4_layouts.module file. This file is not required in Drupal 8 and above, but we’ll need it for later.

Create Row Layout

The Row layout will allow users to add any blocks into 1 to 4 columns using layout builder. The layout is not something specific for the Card component.

1. Create a ww_bootstrap4_layouts.layouts.yml. In this file you define your custom layouts. Add the following into it:

bs_row:
  label: 'BS row'
  path: layouts/bs_row
  template: layout--bs-row
  class: '\Drupal\ww_bootstrap4_layouts\Plugin\Layout\BsRowLayout'
  category: 'WW Bootstrap4'
  default_region: content
  icon_map:
    - [content]
  regions:
    content:
      label: Content

2. Create the following file in the module layouts/bs_row/layout--bs-row.html.twig. You’ll need to create a layouts and bs_row directory.

Add the following Twig code:

{% if content %}
  <div{{ attributes }}>
    {% if content.content %}
      <div {{ region_attributes.content.addClass('layout__region', 'layout__region--content', 'row') }}>
        {{ content.content }}
      </div>
    {% endif %}
  </div>
{% endif %}

3. Create a file called BsRowLayout.php at the path src/Plugin/Layout. Add the following:

<?php

namespace Drupal\ww_bootstrap4_layouts\Plugin\Layout;

use Drupal\layout_builder\Plugin\Layout\MultiWidthLayoutBase;

/**
 * Configurable Bootstrap4 row layout plugin class.
 *
 * @internal
 *   Plugin classes are internal.
 */
class BsRowLayout extends MultiWidthLayoutBase {

  /**
   * {@inheritdoc}
   */
  protected function getWidthOptions() {
    return [
      '1-col' => '1 column',
      '2-col' => '2 column',
      '3-col' => '3 column',
      '4-col' => '4 column',
    ];
  }

  public function build(array $regions) {
    foreach ($this->getPluginDefinition()->getRegionNames() as $region_name) {
      if (array_key_exists($region_name, $regions)) {
        foreach ($regions[$region_name] as $uuid => $block) {
          $regions[$region_name][$uuid]['#attributes']['class'][] = $this->getColumnWidth();
        }
      }
    }

    return parent::build($regions);
  }

  protected function getColumnWidth() {
    $col = [
      '1-col' => 'col-lg-12 mb-4',
      '2-col' => 'col-lg-6 mb-4',
      '3-col' => 'col-lg-4 mb-4',
      '4-col' => 'col-lg-3 mb-4',
    ];

    return $col[$this->configuration['column_widths']];
  }

}

The getWidthOptions() method is used to populate the get “Column widths” drop-down when you configure a section.

The getColumnWidth() method is used to determine the Bootstrap column widths depending on the selection from the drop-down. The returned value from this method is just a Bootstrap class.

Then we override the build() method to add the classes to the inline blocks.

Use new Row Layout

Go and edit a layout and click on “Add section” and you should see the “BS Row” layout on the right.

Select the column option then click on “Add section”.

Then any block added into the section will be displayed as the selected column width.

Fix “Add block” Button

You’ll notice that the “Add block” button in the section isn’t displayed full width. It’s floated to the left or right. Let’s fix that now.

What we need to do is add a column 12 style to the button so it always displays full width. We’ll add the 12 column grid style using Sass.

Bootstrap allows you to apply grid styles via Sass Mixins.

1. Create a file in scss/custom/row/_row.scss in the sub-theme. Add the following into it:

.layout-builder__section {
  .layout-builder__add-block {
    @include make-col-ready();
    @include make-col(12);
  }
}

2. Then add the @import 'custom/row/row'; into scss/style.scss. So it gets compiled.

The button should go from this:

To this:

Create Carousel Component

We’re going to implement the Bootstrap Carousel using two custom block types.

We’ll need to create two block types: Carousel and Carousel item. The Carousel block type will have an entity reference field, referencing the Carousel item.

The Carousel item block type will have a Caption, Image and Title field.

Implement Carousel Item Block Type

First, let’s go and implement the Carousel item block type.

1.  Go to Structure, “Block layout”, “Custom block library”, “Block types” and click on “Add custom block type”.

2. Enter in Carousel item as the label and click on Save.

3. Go and create the three following fields:

  • Caption (field_caption) Text (plain, long)
  • Image (field_image) Media
    Under “Media type” select Image
  • Title (field_title) Text (plain)

4. On the “Manage form display” reorder the widgets like so:

 

Customize Carousel Item Formatters

Now we need to customize the markup that Drupal outputs so it matches the Bootstrap carousel markup. We’ll stick with using a combination of a Field group element and Display Suite for this.

1. Go to “Manage display” on the Carousel item, scroll down to “Layout for carousel item in default” and select “Reset layout”.

2. Click on “Add field group” and select “HTML element” and enter Caption into Label.

3. Add carousel-caption d-none d-md-block into “Extra CSS classes”.

4. Add the field group below Image and indent Title and Caption under the field group.

5. For the Image field make sure you select “Rendered entity” as the formatter. Click on the cog-wheel and select Bare as the View mode and “Full reset” as the field template.

6. Click on the Title cog-wheel and select Expert as the field template and set h5 as the element on the “Field item”.

7. Click on the Caption field cog-wheel and set the field template to Expert and set p as the element on the “Field item”.

Implement Carousel Block Type

Now let’s go ahead and implement the Carousel block type.

1.  Go to Structure, “Block layout”, “Custom block library”, “Block types” and click on “Add custom block type”. Enter in Carousel as the label and click on Save.

2. Click on “Add field”, select Content under Reference from “Add a new field”, and enter in Carousel items as the label.

3. On the field settings page select “Carousel item” from the “Custom block type” checkboxes.

4. We’ll use the Inline entity form module to allow an editor to create carousel items when they add the carousel block using layout builder.

Download and install Inline entity form.

composer require drupal/inline_entity_form

5. Once the above module is installed, go to “Manage form display” on the Carousel block type and select “Inline entity form – Complex” as the widget.

6. Click on the widget cog-wheel and change the labels, Item for singular and Items for plural. And make sure only “Allow users to add new items” is checked.

Then click Update.

At this point, we’ve done all the site building, now we need to roll up our sleeves and write a bit of code.

Create Carousel Layout

To implement the required markup we’ll create a custom layout called “BS carousel”. We’ll add this layout to the existing ww_bootstrap4_layouts module.

1. Open ww_bootstrap4_layouts.layouts.yml and define the new layout:

bs_carousel:
  label: 'BS carousel'
  path: layouts/bs_carousel
  template: layout--bs-carousel
  category: 'WW Bootstrap4'
  default_region: content
  icon_map:
    - [content]
  regions:
    content:
      label: Content

2. Create the layout template at the path, layouts/bs_carousel/layout--bs-carousel.html.twig

{% if content %}
  <div{{ attributes }}>

    {% if content.content %}
      <div {{ region_attributes.content.addClass('layout__region', 'layout__region--content') }}>
        <div id="carousel--{{ carousel_id }}" class="carousel slide" data-ride="carousel">
          <ol class="carousel-indicators">
            {% for item in items %}
              <li data-target="#carousel--{{ carousel_id }}" data-slide-to="{{ loop.index0 }}" class="{{ loop.first ? 'active' }}"></li>
            {% endfor %}
          </ol>
          <div class="carousel-inner">
            {% for item in items %}
            <div class="carousel-item {{ loop.first ? 'active' }}">
              {{ item }}
            </div>
            {% endfor %}
          </div>
          <a class="carousel-control-prev" href="#carousel--{{ carousel_id }}" role="button" data-slide="prev">
            <span class="carousel-control-prev-icon" aria-hidden="true"></span>
            <span class="sr-only">Previous</span>
          </a>
          <a class="carousel-control-next" href="#carousel--{{ carousel_id }}" role="button" data-slide="next">
            <span class="carousel-control-next-icon" aria-hidden="true"></span>
            <span class="sr-only">Next</span>
          </a>
        </div>
        {{ content.content|without('field_carousel_items') }}
      </div>
    {% endif %}

  </div>
{% endif %}

3. Open up ww_bootstrap4_layouts.module and add the following preprocess function:

function ww_bootstrap4_layouts_preprocess_layout__bs_carousel(&$variables) {
  if ($variables['content']['#entity'] instanceof \Drupal\block_content\Entity\BlockContent) {
    /** @var \Drupal\block_content\Entity\BlockContent $block */
    $block = $variables['content']['#entity'];

    if (!$block->get('field_carousel_items')->isEmpty()) {
      $carousel_items = $block->get('field_carousel_items')->referencedEntities();
      foreach($carousel_items as $item) {
        $view_builder = \Drupal::entityTypeManager()->getViewBuilder($item->getEntityTypeId());
        $variables['items'][] = $view_builder->view($item);;
      }
    }

    $variables['carousel_id'] = $block->id();
  }
}

4. Jump back into the “Manage display” on the carousel block type and select “BS carousel”.

Change Large Image Style

For the image in the carousel to look good we need to change the “Large (480×480)”. Go to Configuration, “Image styles” and edit “Large (480×480)”.

Change the scale to 1920×1080.

 

Add Carousel Sass

Create a sass file in the sub-theme, scss/custom/carousel/_carousel.scss and add the following:

.carousel-item {
  img{
    @extend .img-fluid;
  }
}

This will make the image responsive.

Don’t forget to add @import 'custom/carousel/carousel'; into style.scss and recompile.

Create Carousel

1. Go edit a layout and click on “Add section”

2. Select “BS row” and select 1 column.

3. Click on “Add block” and “Create custom block”, then Carousel.

4. Enter in the carousel items then click on “Add block”. If everything has been configured you should see a carousel.

Summary

This video was a little more advanced than the previous one. We implemented two custom layouts, Row and Carousel. I’m happy with the Row layout because we can reuse it with any type of block, not just a card block.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of WebWash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 12 years and has successfully completed several large Drupal projects in Australia.

3 thoughts on “Drupal Live Site Build (Part 2) – Create Layout Builder Section, Bootstrap Carousel”

  1. Bruno Solothurnmann

    Thank you for the notes. The first part I have implemented.
    A small change I had to add on ” 2. Then add the @import ‘custom/row/_row’; into scss/style.scss.” as it has to load _row.

  2. James Fearnley

    I am loving these, thank you!
    Also how good is LANDO! I have continued to use it for this one and even configured xdebug following here https://docs.lando.dev/guides/lando-with-vscode.html (then lando rebuild, install xdebug helper in browser).
    I had to go back and do a few things I missed in the video and not mentioned in the notes to get the bs_rows in the layout working.
    Remove the old themes/ww_bootstrap4/templates folder.
    Install the new module before we can use it.
    Remove the previous layout sections from the last video.

    Will tackle the carousel part of the video soon and give some feedback if I have any.

Leave a Comment

You have to agree to the comment policy.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Media Management in Drupal

Download a FREE 8 part video course on managing media in Drupal.