Exercise 13

In creating a menu pattern with UI Patterns, the main thing it is important to focus is making sure our pattern data matches the structure of Drupal's default menu data.

1. Menu Data

  1. In src/components/ create a new folder called menu.
  2. Inside the menu folder create a new file called menu.ui_patterns.yml
  3. Copy and paste the snippet below into your new file.

menu.ui_patterns.yml

menu:
  label: Main Navigation
  description: The main site menu.
  fields:
    links:
      type: array
      label: Menu Link
      description: Menu links.
      preview:
        -
          url: http://example.com
          title: Home
        -
          url: http://example.com
          title: About
        -
          url: http://example.com
          title: Blog
        -
          url: http://example.com
          title: Contact
  libraries:
    - menu:
       css:
         component:
           menu.css: {}

You'll notice that for each menu item, we are using a URL field and a title field. These fields are what Drupal provides for each menu item. You can reference the default menu.html.twig file from the Classy theme if you want to see a full list of the fields available for each menu item. It's important to use the fields that Drupal provides because it lets us pass the menu from the Drupal template to the pattern.

2. Menu Markup

Like we have done before, create a new file called pattern-menu.html.twig in the src/components/menu directory. Inside of that file, copy and paste the following markup:

pattern-menu.html.twig

<nav class="main-nav">
  <ul {{ attributes.addClass('menu') }}>
    {% for link in links %}
      {% set attributes = create_attribute({}) %}
      {%
        set classes = [
          'menu-item',
          link.is_expanded ? 'menu-item--expanded',
          link.is_collapsed ? 'menu-item--collapsed',
          link.in_active_trail ? 'menu-item--active-trail',
        ]
      %}
      <li {{ attributes.addClass(classes) }}>
        {{ link(link.title, link.url) }}
      </li>
    {% endfor %}
  </ul>
</nav>

This markup is very similar to what you would find inside of menu.html.twig in the Classy theme.

There are a couple special things in here that we haven't seen in other patterns.

Attributes

We include an {{ attributes }} tag on the <ul> for our nav. This allows us to pull in any default or custom attributes that may be set on a menu from Drupal.

We also use the create_attribute() function as we loop through links in the menu. This allows us to add classes to each menu item and have those classes be consistent between our pattern and the Drupal implementation of our pattern.

We use Drupal's link() function to actually create our menu links. This allows us to maintain some features built in to Drupal. You can optionally pass in classes to the link function as well.

3. Menu Styles

To add some basic styles to our menu, go ahead and create src/components/menu/menu.css and copy and past the contents below into the file.

menu.css

.main-nav {
  padding: 1em 0;
}

.main-nav ul {
  display: flex;
  justify-content: center;
  padding: 0;
  margin: 0;
  list-style-type: none;
}

.main-nav .menu-item {
  padding: 0 0.5em;
  margin: 0 0.5em;
}

.main-nav a {
  text-transform: uppercase;
  font-weight: bold;
  letter-spacing: 0.05em;
}

As far as backend work goes, we will use the default Drupal menu system.

Our installation has a Main Navigation menu, so go ahead a create a few menu links in that menu.

Place the Main Navigation in a block so that it is visible on a page.

To integrate our menu, we will use the pattern() function provided by UI Patterns. The pattern function is a Twig function that allows you to render patterns in our Twig templates.

First, inside of src/templates create a file named menu--main.html.twig. We are going to use a template override to override the default menu.html.twig template for our Main navigation.

Calling the pattern function

  • The first parameter is the name of the pattern we want to render. In this case, the name of the pattern is 'menu'.

  • The second parameter is an object that contains any data we want to send into our pattern template. By default, Drupal provides the items variable inside of menu templates. The items variable is a nested list of menu items. If you recall from our pattern template, we called our list of menu items links, so we need to pass the items list into our template, but named as links. This is an important part of working with UI Patterns: you can rename variables as you pass them into your patterns, which makes your patterns much more reusable!

  • We also pass in the attributes from the attributes variable, to make sure we capture any added attributes in our pattern.

menu--main.html.twig

{{
  pattern('menu', {
    links: items,
    attributes: attributes
  })
}}

That's all the integration needed for this pattern. Once you save and clear your cache, you will see the main navigation on the site is now implementing the menu pattern we created.

Extra Credit

Active Menu Trail

We've exposed some classes (.menu-item--active-trail , .is-active) in our pattern that Drupal will use to tell us if the menu item is a link to the page we are currently on. Try creating a larger menu and expirmenting with these classes.

Nested Menu Items

So far, we've only implemented a single level of menu, but Drupal supports nesting of menu items as well. To work with nested menu items, you'll need to create menu items that are children of other menu items in your menu.

Then, in your menu block, you'll need to configure the Menu Levels settings to display more than one level of the menu.

We'll also have to update our menu pattern to deal with the nesting. If a menu is expanded and has children, we can check to see if the below attribute is populated. Then, we can nest a pattern inside our pattern. In fact, you can nest the current pattern inside itself! Experiment with how you might use this for a complex menu. Here's an example of how your pattern might look with nested menus:

<nav class="main-nav">
  <ul {{ attributes.addClass('menu') }}>
    {% for link in links %}
      {% set attributes = create_attribute({}) %}
      {%
        set classes = [
          'menu-item',
          link.is_expanded ? 'menu-item--expanded',
          link.is_collapsed ? 'menu-item--collapsed',
          link.in_active_trail ? 'menu-item--active-trail',
        ]
      %}
      <li {{ attributes.addClass(classes) }}>
        {{ link(link.title, link.url) }}
        {# Check if the link has submenu items. #}
        {% if link.below %}
          {{ pattern('menu', {
            'links': link.below
          }) }}
        {% endif %}
      </li>
    {% endfor %}
  </ul>
</nav>

results matching ""

    No results matching ""