On Github backlineint / testing-d8-theme
Chasing The Dream of Style Guide Driven Development
One tiny css change causes a regression deep in the outer reaches of your site
(and, it goes unnoticed for lord knows how long)
* Maybe it's an honest mistake * Maybe someone new to the codebase is working in the theme layer and doesn't have the context you do.Base Themes
{%
set classes = [
'block',
'block-' ~ configuration.provider|clean_class,
'block-' ~ plugin_id|clean_class,
]
%}
< div {{ attributes.addClass(classes) }}>
(From block.html.twig)
{% if attributes.hasClass('myClass') %}
{# do stuff #}
{% endif %}
* More classes and markup in templates, less in preprocessor and theme functions. So much easier to find stuff and create cleaner, well organized markup.
* No PHP in templates
* Don't preprocess to add classes, use Twig helper function.
* Todo: Screen shot this.
Naming convention for components
.block {}
.block__element {}
.block--modifier {}
.block__element--modifier {}
* Block - object (component) on your website
* Element - Sub-component of a block that performs a particular function. Should only make sense within the context of the block
* Modifier - Variations of block or element
links--node.html.twig (classy)
{% if links %}
<div class="node__links">
{% include "links.html.twig" %}
</div>
{% endif %}
links.html.twig (classy)
{% if links -%}
{%- if heading -%}
{%- if heading.level -%}
<{{ heading.level }}{{ heading.attributes }}>{{ heading.text }}<!--{{ heading.level }}-->
{%- else -%}
<h2{{ heading.attributes="" }}="">{{ heading.text }}
{%- endif -%}
{%- endif -%}
<ul{{ attributes="" }}="">
{%- for item in links -%}
<li{{ item.attributes="" }}="">
{%- if item.link -%}
{{ item.link }}
{%- elseif item.text_attributes -%}
<span{{ item.text_attributes="" }}="">{{ item.text }}
{%- else -%}
{{ item.text }}
{%- endif -%}
{%- endfor -%}
{%- endif %}
</span{{></li{{></ul{{></h2{{>
* Have access to variables and can map them
field.html.twig
* Twig allows you to extend templates, which can really help with re-use. * Imagine you want to wrap the field in a different tag. In D7 if you wanted to do this in the template, you'd have to create a copy of the entire field template * Todo: fix image overflow or use code.Custom field.html.twig
* If you define elements as blocks, you can extend them. * Here I've created a custom version of the field template where I've defined blocks. * Todo: fix image overflow or use code.field--field-hero-header.html.twig
* Now for a specific field template I can re-define the blocks, not the entire template.Combines Include and Extends
{% embed "teasers_skeleton.twig" %}
{# These blocks are defined in "teasers_skeleton.twig" #}
{# and we override them right here: #}
{% block left_teaser %}
Some content for the left teaser box
{% endblock %}
{% block right_teaser %}
Some content for the right teaser box
{% endblock %}
{% endembed %}
* Can use multiple embeds
Can now add libraries within twig templates
{{ attach_library('contextual/drupal.contextual-links') }}
<div>Some markup {{ message }}</div>
Careful - can impact aggregation / performance
https://www.drupal.org/project/styleguide
Many options
npm install kss --save-dev
npm install grunt-kss --save-dev
// Cards
//
// Multiple cards included in a grid
//
// Markup: cards.twig
//
// Style Guide: Patterns.Cards
.cards {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
* Name
* Description
* Markup - can be file location (html, handlebars, twig) or inline if simple markup
* Category in Style Guidegrunt kss
or:
kss-node --source sass --destination styleguide --css ../css/style.css --builder=builder/twig
Behavior Driven Development Framework
@javascript Scenario: Footer style matches style guide Given I am an anonymous user And I am on "assessments/practice-assessments" When I am browsing using a "phone" Then "body" should have a "background-color" css value of "rgb(112, 84, 125)" And "#page" should have a "background-color" css value of "rgb(255, 255, 255)" And "footer" should have a "padding-top" css value of "50px" And "footer" should have a "padding-bottom" css value of "30px"
View Gist for detail of steps
* Great for functional testing, not as great for testing look and feel. * Screenshot options are limited, especially comparison * Created custom rules to test computed CSS values, but have to get really specific and values can drift when things like scrollbars are introduced.Screenshot Comparison Tool
Selenium Bindings for NodeJS
Strengths: interactivity, easier to target components.
npm install webdriverio@3.4.0 webdrivercss@2.0.0beta-rc1 selenium-standalone --save-dev
./node-modules/.bin/selenium-standalone install
Also requires graphicsmagick
* Have considered using the mainline version, but the Grunt version works so well and is so easy to set up...wdio-tests.js
var wdio = require("webdriverio");
var webdrivercss = require("webdrivercss");
var assert = require("assert");
var options = {
desiredCapabilities: {
browserName: "chrome"
}
}
var browser = wdio.remote(options);
webdrivercss.init(browser, {
screenshotRoot: "screenshots"
});
function assertShots (err, shots) {
assert.ifError(err);
Object.keys(shots).forEach(function(element) {
shots[element].forEach(function(shot) {
assert.ok(shot.isWithinMisMatchTolerance, shot.message);
})
});
};
* Can execute js before screenshots - chaning out fonts or content
* Takes a named screenshot based on a selector.
browser
.init()
.url("http://testing-d8-theme.dd:8083/cards")
.webdrivercss("cards", [
{
name: "cards",
elem: ".cards"
}
], assertShots)
.end();
* Can execute js before screenshots - chaning out fonts or content
* Takes a named screenshot based on a selector.
Some simple interactivity
browser
.init()
.url(config.url)
.click(".fa-bars")
.webdrivercss("navigation", [
{
name: "Off Canvas Menu",
elem: ".sidebar-offcanvas"
}
], assertShots)
.end();
* Can execute js before screenshots - chaning out fonts or content
* Takes a named screenshot based on a selector.
* Requires Components Module *
card.twig
<div class="card">
<div class="card-image">
<img src="{{ img_src }}" alt="{{ img_alt }}">
</div>
<div class="card-header">
{{ header }}
</div>
<div class="card-copy">
<p>{{ copy }}</p>
</div>
</div>
node--article--card.twig
{% include "@components/card/card.twig"
with {
"img_src": file_url(node.field_image.entity.fileuri),
"img_alt": content.field_image.0['#item'].alt,
"header": label,
"copy": content.body
}
%}