shibin das
Drupal Swashbuckler
by
Shibin Das
www.drupal.org/u/d34dman
About
We establish boundaries with clear responsibilities.
It also created the WALL.
THEM
YOU
THEM
YOU
+
Carbon Design Systems Component library
+
+
+
NOTE: "Miyagi" may be used in examples, but in reality it doesn't matter which Design System is used.
Must be Familiar with
Bonus
Patternlab.io Website
docs.miyagi.dev Website
Must be Familiar with
Why
Why
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
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
GREEN
Drupal Template files
teaser-list.html.twig
{% include "@template/teaser-list/teaser-list.twig" with teaser_list_data %}Twig Templates
BLUE
Preprocess Function (Drupal)
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
ORANGE
Drupal UI Configurations
Click Click Click
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.html.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.html.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.html.twig
{% set main_content %}
{{ page.content_pre }}
{{ page.content }}
{{ page.content_post }}
{% endset %}
{% include "@templates/default/default.twig" with
{
content: main_content,
messages: messages
} only
%}page.html.twig
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 component_data 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>
@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 component_data only %}bc_utils.module
<?php
/**
* Preprocess Article Node.
*/
function bc_utils_preprocess_node_article(&$vars) {
$vars['component_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['component_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 component_data only %}bc_utils.module
<?php
/**
* Preprocess Article Node.
*/
function bc_utils_preprocess_node_article(&$vars) {
$vars['component_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 component_data %}bc_utils.module
<?php
/**
* Preprocess Article Node.
*/
function bc_utils_preprocess_node_article(&$vars) {
$vars['component_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.html.twig
{% include "@template/teaser-list/teaser-list.twig" with teaser_list only %}node--article--teaser.html.twig
{% include "@template/article-teaser/article-teaser.twig" with component_data only %}@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.html.twig
{% include "@template/article-teaser/article-teaser.twig" with component_data only %}@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 only %}@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.
It aligns witch how Drupal templates works to start with.
As a bonus, 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
Info
$foo = [
'#type' => 'markup',
'#markup' => 'I am just a string',
];{{ foo }}i am just a stringRender array (php)Twig TemplateHTML Output$foo = [
'#type' => 'markup',
'#markup' => 'I am just a string',
'#access' => FALSE,
];{{ foo }} $foo = [
'#type' => 'markup',
'#markup' => 'I am just a string',
'#prefix' => '<div class="foo">',
'#suffix' => '</div>'
];{{ foo }}<div class="foo">i am just a string</div> $foo = t('Hello @name!', ['@name' => 'World']);{{ foo }}Hello World!Render array (php)Twig TemplateHTML OutputDrupal's "t" function generates a render array.
{{ foo|raw }}Info
Integrating
and other cases
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
bc_utils.module
$vars['hasBorder'] = $entity->field_has_border->getValue();BY NOT Passing the render array of the field found under $vars['content']
@organisms/ste/ste.twig
<div class="Block SimpleTextLink {{ hasBorder ? 'SimpleTextLink--hasBorder' : '' }}">
...
</div>
When you check the variable in twig template,
most likely you can't use a render array
By shibin das
Working with Drupal and Design System