by
Shibin Das
www.drupal.org/u/d34dman
Printed Circuit Board
Could you spot an odd one out?
Text
Orienting the components in a similar manner help prevent assembly error
Or at least makes spotting errors much easier.
Text
Following a convention helps in
quality control
Having a wide collection of screwdrivers doesn't justify the use of wide types of screwdrivers
Must be Familiar with
Bonus
Twig for Drupal 7 Website
Must be Familiar with
Bonus
Patternlab.io Website
Mockups
Using Balsamiq Mockups 3
@template/teaser-list/teaser-list.twig
<div class="TeaserList">
{% for item in items %}
<div class="TeaserList-item">
{{ item|raw }}
</div>
{% endfor %}
</div>
Patternlab files
Twig templates
Drupal Template files
teaser-list.tpl.twig
{% include "@template/teaser-list/teaser-list.twig" with teaser_list_data %}Twig Templates
Drupal Module/Theme Preprocessing Files
bc_utils.module
<?php
/**
* Implements hook_block_view().
*/
function bc_utils_block_view($delta) {
$block = [];
switch ($delta) {
case 'teaser_list':
// Retrieve list of article nodes using Views or Entity Field Query.
$article_nodes = _bc_utils_get_article_teaser_list_nodes($max_items = 3);
$items = [];
foreach ($article_nodes as $article_node) {
$items[] = node_view($article_node, 'teaser');
}
// Prepare block render array.
$block['subject'] = t('Teaser List');
$block['content'] = [
'#theme' => 'teaser_list',
'#teaser_list_data' => [
'items' => $items,
],
];
}
return $block;
}PHP Code
Drupal UI Configurations
Click Click Click
Human Mind is amazing
because it Ignores
Integrating
Lets say you have some designs that needs to be integrated into Drupal
Frontend Developers (FE) starts working in Patternlab
Get your components reviewed in Patternlab with demo data
Use Drupal Templates to link Patternlab components with Drupal
Pass variables from Drupal to Patternlab through theme_preprocess functions
Integrating
Integrating
Header
Footer
Sidebar
Content
Breadcrumb
<!DOCTYPE html>
<html lang="{{ language.language }}" dir="{{ language.dir }}">
<head>
{{ head }}
<title>{{ head_title }}</title>
{{ styles }}
</head>
<body class="{{ classes }}" {{ attributes }}>
<div class="u-hiddenVisually" id="skip-link">
<a href="#main-content" class="element-invisible element-focusable">
{{ 'Skip to main content'|t }}
</a>
</div>
{{ page_top }}
{{ page }}
{{ page_bottom }}
{{ scripts }}
</body>
</html>html.tpl.twig
{% include '@template-components/header/header.twig' %}
{% include '@template-components/messages/messages.twig' with messages %}
{{ page.content_pre }}
{{ page.content }}
{{ page.content_post }}
{% include '@template-components/footer/footer.twig' %}page.tpl.twig
{% extends "@templates/default/default.twig" %}
{% block messages %}
{% include '@template-components/messages/messages.twig' with messages %}
{% endblock %}
{% block content %}
{{ page.content_pre }}
{{ page.content }}
{{ page.content_post }}
{% endblock content %}
page.tpl.twig
{% extends "@templates/default/default.twig" %}
{% block messages %}
{% include '@template-components/messages/messages.twig' with messages %}
{% endblock %}
{% block sidebar %}
{{ sidebar }}
{% endblock sidebar %}
{% block content %}
{{ page.content_pre }}
{{ page.content }}
{{ page.content_post }}
{% endblock content %}
page.tpl.twig
Why not use "include" always
Integrating
<div class="Article">
<div class="Article-header">
<h1 class="Article-title">
{{ title }}
</h1>
<div class="Article-subTitle">
{{ sub_title }}
</div>
</div>
<div class="Article-content">
<div class="Article-video">
{{ video }}
</div>
<div class="Article-description">
{{ description }}
</div>
</div>
</div>
@template/article/article.twig
@template/article/article.twig
<div class="Article">
<div class="Article-header">
<h1 class="Article-title">
{{ title|raw }}
</h1>
<div class="Article-subTitle">
{{ sub_title|raw }}
</div>
</div>
<div class="Article-content">
<div class="Article-video">
{{ video|raw }}
</div>
<div class="Article-description">
{{ description|raw }}
</div>
</div>
</div>
node--article.tpl.twig
{% include "@template/article/article.twig" with patternlab_data %}@template/article/article.twig
<div class="Article">
<div class="Article-header">
<h1 class="Article-title">
{{ title|raw }}
</h1>
<div class="Article-subTitle">
{{ sub_title|raw }}
</div>
</div>
<div class="Article-content">
<div class="Article-video">
{{ video|raw }}
</div>
<div class="Article-description">
{{ description|raw }}
</div>
</div>
</div>
WITH ONLY!!!!!
@template/article/article.twig
<div class="Article">
<div class="Article-header">
<h1 class="Article-title">
{{ title|raw }}
</h1>
<div class="Article-subTitle">
{{ sub_title|raw }}
</div>
</div>
<div class="Article-content">
<div class="Article-video">
{{ video|raw }}
</div>
<div class="Article-description">
{{ description|raw }}
</div>
</div>
</div>
node--article.tpl.twig
{% include "@template/article/article.twig" with patternlab_data %}bc_utils.module
<?php
/**
* Preprocess Article Node.
*/
function bc_utils_preprocess_node_article(&$vars) {
$vars['patternlab_data'] = [
'title' => $vars['node']->title,
'sub_title' => $vars['content']['field_article_sub_title'],
'video' => $vars['content']['field_article_video'],
'description' => $vars['content']['field_article_description'],
];
}
@template/article/article.twig
<div class="Article">
<div class="Article-header">
<h1 class="Article-title">
{{ title|raw }}
</h1>
<div class="Article-subTitle">
{{ sub_title|raw }}
</div>
</div>
<div class="Article-content">
<div class="Article-video">
{{ video|raw }}
</div>
<div class="Article-description">
{{ description|raw }}
</div>
</div>
</div>
bc_utils.module
<?php
/**
* Preprocess Article Node.
*/
function bc_utils_preprocess_node_article(&$vars) {
$vars['patternlab_data'] = [
'title' => $vars['node']->title,
'sub_title' => $vars['content']['field_article_sub_title'],
'video' => $vars['content']['field_article_video'],
'description' => $vars['content']['field_article_description'],
];
}
@template/article/article.twig
<div class="Article">
<div class="Article-header">
<h1 class="Article-title">
{{ title|raw }}
</h1>
<div class="Article-subTitle">
{{ sub_title|raw }}
</div>
</div>
<div class="Article-content">
<div class="Article-video">
{{ video|raw }}
</div>
<div class="Article-description">
{{ description|raw }}
</div>
</div>
</div>
node--article.tpl.twig
{% include "@template/article/article.twig" with patternlab_data %}bc_utils.module
<?php
/**
* Preprocess Article Node.
*/
function bc_utils_preprocess_node_article(&$vars) {
$vars['patternlab_data'] = [
'title' => $vars['node']->title,
'sub_title' => $vars['content']['field_article_sub_title'],
'video' => $vars['content']['field_article_video'],
'description' => $vars['content']['field_article_description'],
];
}
@template/article/article.twig
<div class="Article">
<div class="Article-header">
<h1 class="Article-page-title">
{{ title | raw }}
</h1>
<div class="Article-page-subtitle">
{{ sub_title | raw }}
</div>
</div>
<div class="Article-content">
<div class="Article-content-video">
{{ video | raw }}
</div>
<div class="Article-content-description">
{{ description | raw }}
</div>
</div>
</div>
node--article.tpl.twig
{% include "@template/article/article.twig" with patternlab_data %}bc_utils.module
<?php
/**
* Preprocess Article Node.
*/
function sscild_frontend_preprocess_node_article(&$vars) {
$vars['patternlab_data'] = [
'title' => $vars['node']->title,
'sub_title' => $vars['content']['field_article_sub_title'],
'video' => $vars['content']['field_article_video'],
'description' => $vars['content']['field_article_description'],
];
}
@template/article/article.twig
<div class="Article">
<div class="Article-header">
<h1 class="Article-title">
{{ title|raw }}
</h1>
<div class="Article-subTitle">
{{ sub_title|raw }}
</div>
</div>
<div class="Article-content">
<div class="Article-video">
{{ video|raw }}
</div>
<div class="Article-description">
{{ description|raw }}
</div>
</div>
</div>
node--article.tpl.twig
{% include "@template/article/article.twig" with patternlab_data %}bc_utils.module
<?php
/**
* Preprocess Article Node.
*/
function bc_utils_preprocess_node_article(&$vars) {
$vars['patternlab_data'] = [
'title' => $vars['node']->title,
'sub_title' => $vars['content']['field_article_sub_title'],
'video' => $vars['content']['field_article_video'],
'description' => $vars['content']['field_article_description'],
];
}
Integrating
@template/teaser-list/teaser-list.twig
<div class="TeaserList">
{% for item in items %}
<div class="TeaserListt-item">
<div class="TeaserList-item-Article-teaser">
<h3>{{ item.title | raw }}</h3>
<h4>{{ item.sub_title | raw}}</h4>
<div class="TeaserList-item-Article-teaser-description">
{{ item.description | raw }}
</div>
</div>
</div>
{% endfor %}
</div>
@template/teaser-list/teaser-list.twig
<div class="TeaserList">
{% for item in items %}
<div class="TeaserList-item">
{{ item | raw }}
</div>
{% endfor %}
</div>
@template/article-teaser/article-teaser.twig
<div class="Article-teaser">
<h3>{{ title | raw }}</h3>
<h4>{{ sub_title | raw}}</h4>
<div class="Article-teaser-description">
{{ description | raw }}
</div>
</div>block--teaser-list.tpl.twig
{% include "@template/teaser-list/teaser-list.twig" with teaser_list %}node--article--teaser.tpl.twig
{% include "@template/article-teaser/article-teaser.twig" with patternlab_data %}@template/teaser-list/teaser-list.twig
<div class="TeaserList">
{% for item in items %}
<div class="TeaserList-item">
{{ item | raw }}
</div>
{% endfor %}
</div>
@template/article-teaser/article-teaser.twig
<div class="ArticleTeaser">
<h3>{{ title | raw }}</h3>
<h4>{{ sub_title | raw}}</h4>
<div class="ArticleTeaser-description">
{{ description | raw }}
</div>
</div>node--article--teaser.tpl.twig
{% include "@template/article-teaser/article-teaser.twig" with patternlab_data %}@template/article-teaser/article-teaser.twig
<div class="ArticleTeaser">
<h3>{{ title | raw }}</h3>
<h4>{{ sub_title | raw}}</h4>
<div class="ArticleTeaser-description">
{{ description | raw }}
</div>
</div>teaser-list.tpl.twig
{% include "@template/teaser-list/teaser-list.twig" with teaser_list_data %}@template/teaser-list/teaser-list.twig
<div class="TeaserList">
{% for item in items %}
<div class="TeaserList-item">
{{ item | raw }}
</div>
{% endfor %}
</div>
bc_utils.module
<?php
/**
* Implements hook_block_view().
*/
function bc_utils_block_view($delta) {
$block = [];
switch ($delta) {
case 'teaser_list':
// Retrieve list of article nodes using Views or Entity Field Query.
$article_nodes = _bc_utils_get_article_teaser_list_nodes($max_items = 3);
$items = [];
foreach ($article_nodes as $article_node) {
$items[] = node_view($article_nodes, 'teaser');
}
// Prepare block render array.
$block['subject'] = t('Teaser List');
$block['content'] = [
'#theme' => 'teaser_list',
'#teaser_list_data' => [
'items' => $items,
],
];
}
return $block;
}teaser-list.tpl.twig
{% include "@template/teaser-list/teaser-list.twig" with teaser_list_data %}@template/teaser-list/teaser-list.twig
<div class="Teaser-list">
{% for item in items %}
<div class="Teaser-list-item">
{{ item | raw }}
</div>
{% endfor %}
</div>
bc_utils.module
<?php
/**
* Implements hook_block_view().
*/
function bc_utils_block_view($delta) {
$block = [];
switch ($delta) {
case 'teaser_list':
// Retrieve list of article nodes using Views or Entity Field Query.
$article_nodes = _bc_utils_get_article_teaser_list_nodes($max_items = 3);
$items = [];
foreach ($article_nodes as $article_node) {
$items[] = node_view($article_nodes, 'teaser');
}
// Prepare block render array.
$block['subject'] = t('Teaser List');
$block['content'] = [
'#theme' => 'teaser_list',
'#teaser_list_data' => [
'items' => $items,
],
];
}
return $block;
}teaser-list.tpl.twig
{% include "@template/teaser-list/teaser-list.twig" with teaser_list_data %}@template/teaser-list/teaser-list.twig
<div class="TeaserList">
{% for item in items %}
<div class="TeaserList-item">
{{ item | raw }}
</div>
{% endfor %}
</div>
bc_utils.module
<?php
/**
* Implements hook_block_view().
*/
function bc_utils_block_view($delta) {
$block = [];
switch ($delta) {
case 'teaser_list':
// Retrieve list of article nodes using Views or Entity Field Query.
$article_nodes = _bc_utils_get_article_teaser_list_nodes($max_items = 3);
$items = [];
foreach ($article_nodes as $article_node) {
$items[] = node_view($article_nodes, 'teaser');
}
// Prepare block render array.
$block['subject'] = t('Teaser List');
$block['content'] = [
'#theme' => 'teaser_list',
'#teaser_list_data' => [
'items' => $items,
],
];
}
return $block;
}
Making List component agnostic of its embedded items opens up a lot of possibility.
For example we can now use the same teaser-list component to show list of teasers of different content types. These content types can have different fields and teaser template.
The same logic can be applied for rendering fields which embed content on entities through
Integrating
/**
* Implements hook_preprocess_node().
*/
function my_module_preprocess_node(&$vars) {
// A simple text field can be found in following places in variable $vars.
// Inside the entity object.
// CASE 1:
$description = $vars['elements']['#node']->field_description['und'][0]['value'];
// CASE 2:
$description = $vars['elements']['#node']->field_description['und'][0]['safe_value'];
// The following is a render array.
// Present if the particular viewmode is configured to show the field.
// CASE 3:
$description = $vars['content']['field_description'];
}/**
* Implements hook_preprocess_node().
*/
function my_module_preprocess_node(&$vars) {
// A simple text field can be found in following places in variable $vars.
// Inside the $vars as fields, present when there is a value stored in database.
// CASE 4:
$description = $vars['field_description'][0]['value'];
// CASE 5:
$description = $vars['field_description'][0]['safe_value'];
}/**
* Implements hook_preprocess_node().
*/
function my_module_preprocess_node(&$vars) {
// A simple text field can be found in following places in variable $vars.
// You can use entity_metadata_wrapper to retrieve the property.
try {
$node = $vars['elements']['#node'];
$wrapper = entity_metadata_wrapper('node', $node);
// CASE 6:
$description = $wrapper->field_description->value();
// Which also allows you to fetch referenced entity and their properties too.
foreach ($wrapper->field_taxonomy_terms->getIterator() as $delta => $term_wrapper) {
$label = $term_wrapper->name->value();
}
}
catch (EntityMetadataWrapperException $exc) {
$trace = '<pre>' . $exc->getTraceAsString() . '</pre>';
watchdog(
‘debug’,
$trace,
NULL,
WATCHDOG_ERROR
);
}
}/**
* Implements hook_preprocess_node().
*/
function my_module_preprocess_node(&$vars) {
// A simple text field can be found in following places in variable $vars.
// ---------------------- //
// Or you might even do a db_query/db_select.
// CASE 7:
}bc_utils.module
$vars['sub_title'] = $vars['content']['field_article_sub_title'];Pass the render array of the field found under $vars['content']
@template/article/article.twig
{{ sub_title | raw }}In patternlab component print the variable using "raw"
Field Item 1
Field Wrapper
{{ field_article text }}
Field Item 1
Field Wrapper
{{ field_article text }}
Field Item 2
Field Item 3
Field Item 1
Field Wrapper
{{ field_article text }}
Field Item 2
Field Wrapper
Field Item 2
Field Wrapper
bc_utils.module
$vars['references'] = array();
if (!empty($vars['content']['field_references'])) {
foreach (element_children($vars['content']['field_references']) as $delta) {
$vars['references'][] = $vars['content']['field_references'][$delta];
}
}Pass the render array of the field found under $vars['content']
after a the following adjustment
@template/article/article.twig
<div class="ArticleReferences-list">
{% for reference in references %}
<div class="ArticleReference-list-item">
{{ reference | raw }}
</div>
{% endfor %}
</div>This will allow access to multivalued field as an array in patternlab component
Following fields can use the strategy
Integrating
bc_utils.module
$vars['cta'] = array(
'label' => $vars['fpp_rel_story_cta'][0]['title'],
'url' => url($vars['fpp_rel_story_cta'][0]['url']),
);BY NOT Passing the render array of the field found under $vars['content']
@organisms/ste/ste.twig
<div class="Block SimpleTextLink">
{% include "@atoms/button/button.twig" with {
url: cta.url,
label: cta.label,
target: "_blank"
} %}
</div>
In patternlab component you can access the variable using custom property