-
Notifications
You must be signed in to change notification settings - Fork 70
Adds function to replace large class functionality in each component #78
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@evanmwillhite I'm assuming the answer is yes, but I want to clarify, can we also just do:
And, does the order matter? e.g. Could I do this:
I also just noticed that you have |
We need to discuss this one before merging because of the ramifications on our Drupal work, but to answer your questions @ModulesUnraveled:
|
I would propose you rename this function to |
/** | ||
* @file | ||
* Add "classes" function for Pattern Lab. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add some params here? would help address @ModulesUnraveled's question regarding 'COMPONENT_NAME'
(string) vs COMPONENT_MODIFIERS
(array with no quotes)
As discussed in PG, let's see if we can:
|
} | ||
if ($blockname) { | ||
$classes[] = $blockname . '__' . $base_class; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the blockname
conditional, I wonder if the modifiers loop should be repeated. It's a pattern I rely on frequently enough to standardize. Then you can target a modified component within a parent like so:
// abstract
.blockname__baseclass--modifier {}
// example
.bios__profile--badge-webchef {}
I have implemented this in a project and the logic works as advertised. 👍 |
This is a great idea and something we need but there is a blocker for this to be compatible with Drupal: We could create a sister Emulsify module to provide custom functions and filters until themes can provide their own. But right now this will appear to work within Pattern Lab but then getting it into Drupal will require setting up a custom module (granted it will be small). Example here if this needs to get into an existing client project: https://drupal.stackexchange.com/questions/184184/error-when-creating-a-custom-twig-filter |
Yeah there's precedent for having companion modules to themes. The original I know of is Omega Tools and there is also Magic which was intended as more of a shared module for several themes. The PR linked after my previous comment is not visible to the public, but I started work in another 4K client repo and when it works we can use that as a starter. @ccjjmartin you pasted the 8.2.x link; a word of warning that 8.3.x is significantly different. Plus Drupal 8.4 was released yesterday. Just a heads up if someone decides to dive in.. don't use the old version! I got snagged on that while setting it up. |
Something that had never occurred to me until I was fixing linting errors on this class I'm writing.. shouldn't the args be in a different order? It's called BEM but we're specifying EMB. Changing the order, plus the variable name |
OK, I have a lot to say here - sorry I realized as I was writing it out how long-winded it is. :) As far as an add-on module goes, I'm not crazy about the idea as it's yet another thing for Four Kitchens to support and it's currently a trivial amount of functionality to justify it (versus the modules listed above). If we could figure out a way to handle the classes correctly in PL and then deliver them as a string to Drupal attributes that would be ideal (only needing the Twig function on the PL side and therefore no Drupal module), but I can't think of how to do that off the top of my head. If we do go the module route, we have a couple of options:
As for the BEM order,
Here, "card" is the block name. When using
This is actually a non-standard BEM usage because there are now 2 blocknames (and 2 styles will be inherited). I think I personally would rather see us either:
or 2. have the override replace the original like so (and use mixins, etc. to encapsulate any heading styles that you want to "inherit" in multiple places):
In writing this out, I actually think the 2nd option is what we need to do as it is true BEM and is therefore more readable/approachable while also allowing for the flexibility we like in being able to pass in a new block name. In terms of where we go from here, I see the issue about handling Drupal attributes as being directly related to this PR. I think we should possibly rewriting this Twig function so that the following syntax (or something like it) would create the correct classes in both places:
This would mean adding some functionality to the function to check whether Drupal attributes are in play and create this for PL:
and this for Drupal:
This above to me would justify the module, because it would solve both problems and be a really great addition to the wider Drupal/PL community. What do y'all think? |
@evanmwillhite I was testing this last night in Drupal and ran into the issue that the Twig Parser class says the function doesn't exist. Example: we name the function bem(). When Drupal does the include on the twig file, bem() will fatal error (WSOD) with a message about bem() is not a declared function. So any non Drupal functions in Pattern Lab will have to be included within Drupal. If there is a module that searches Pattern lab and includes automatically (ideal solution) we should list that module as a dependency to unblock this and other twig functions. |
Yea @ccjjmartin that's why this is not merged in yet - because it's currently a blocker for Drupal. If we can't write this as a function that only Pattern Lab would need (which I can't personally think of a way to do), I'd like to see us rewrite this function to print classes for PL and the correct attributes syntax for Drupal (see the last part of my comment above) and release both open-source (neither are Emulsify-specific). |
whew! that is a lot. I have no strong opinions about the BEM really, the element thing was just a random thought I had. I also think a new module to supply the one twig function is overkill, maybe just some documentation or a nice detailed blog post would suffice for helping people implement in Drupal. |
That was fun the read, y'all! 💯 |
@@ -8,6 +8,6 @@ | |||
}, | |||
"scripts": { | |||
"start": "gulp clean && gulp compile && gulp theme", | |||
"postinstall": "rm -rf pattern-lab && composer create-project -n pattern-lab/edition-twig-standard pattern-lab && rm -rf pattern-lab/source && ln -s ../components pattern-lab/source" | |||
"postinstall": "rm -rf pattern-lab && composer create-project -n pattern-lab/edition-twig-standard pattern-lab && rm -rf pattern-lab/source && ln -s ../components pattern-lab/source && git clone https://github.com/drupal-pattern-lab/bem-twig-extension.git && cp bem-twig-extension/bem.function.php components/_twig-components/functions && rm -rf bem-twig-extension/" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to consider writing a small node script for this using https://www.npmjs.com/package/fs-extra and https://www.npmjs.com/package/cross-spawn so that emulsify will work on windows machines. If you want help with this, I can carve out some time!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great idea - I created an issue over on emulsify-gulp and assigned you but feel free to move wherever fourkitchens/emulsify-gulp#34
#} | ||
{% set h1_base_class = h1_base_class|default('h1') %} | ||
|
||
<h1{{ bem(h1_base_class, (modifiers), blockname) }}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, one thing I found out using this on Ithaca, is that if we just use 'modifiers' at all levels, it gets passed down the chain. So, for example, if we set modifiers = [alt, light]
to get class="h1-alt h1-light
they'll also get passed down to the link component because it also uses the variable modifiers
. So we'll end up with something like this:
<h1 class="h1-alt h1-light">
<a class="link-alt link-light></a>
</h1>
(I've intentionally left out the href etc. to focus on the classes)
I didn't want that in my project, so I used the following in my link and h1 files respectively
<a{{ bem(link_base_class, (link_modifiers), link_blockname) }}>
and
+<h1{{ bem(heading_base_class, (heading_modifiers), heading_blockname) }}>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I wondered if I would have to do this. Done!
@@ -1,6 +1,6 @@ | |||
grid_label: | |||
"2-Column Divider Example" | |||
grid_modifiers: | |||
modifiers: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you agree with my last comment about variable inheritance, these should be reverted.
// With Columns | ||
.grid__item { | ||
.grid--columns-2 & { | ||
@if $columns == 2 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting! I'll have to try to remember you can do this!
|
||
<a class="{{ link_base_class_var }}{% for modifier in link_modifiers %} {{ link_base_class_var }}--{{ modifier }}{% endfor %}{% if link_blockname %} {{ link_blockname }}__{{ link_base_class_var }}{% endif %}" {% for attribute,value in link_attributes %}{{ attribute }}="{{ value }}"{% endfor %} href="{{ link_url }}"> | ||
<a{{ bem(link_base_class, (modifiers), blockname) }}{% for attribute,value in link_attributes %} {{ attribute }}="{{ value }}"{% endfor %} href="{{ link_url }}"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these components can span multiple lines because of the way twig handles spaces. It makes it easier to read (classes on one line, attributes on another, href on a third) and tweaks are better represented in diffs since it's not all one line) Here's an example from one of my projects:
{% set link_base_class_var = link_base_class|default('link') %}
<a
{{ bem(link_base_class_var, (link_modifiers), link_blockname) }}
{% for attribute,value in link_attributes %}{{ attribute }}="{{ value }}"{% endfor %}
href="{{ link_url }}"
>
{% block link_content %}
{{ link_content }}
{% endblock %}
</a>
Image is another good use-case:
<img
{{ bem(link_base_class_var, (link_modifiers), link_blockname) }}
src="{{ image_src }}"
{% if image_alt %}
alt="{{ image_alt }}"
{% else %}
aria-hidden="true"
{% endif %}
>
Link and image are good examples for this since they have multiple distinct parts. If a component only defines classes, I might not do it. e.g. this would look funny and be overkill IMO:
<p
{{ bem(link_base_class_var, (link_modifiers), link_blockname) }}
>
{% block paragraph_content %}
{{ paragraph_content }}
{% endblock %}
</p>
I'd just put the bem function inline with the
tag and save two lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this and have implemented it where it made sense
@@ -11,9 +11,9 @@ | |||
* for example: A formatted text field in Drupal | |||
*/ | |||
#} | |||
{% set paragraph_base_class_var = paragraph_base_class|default('paragraph') %} | |||
{% set paragraph_base_class = paragraph_base_class|default('paragraph') %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work? Or does it need to be a separate variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works great!
@@ -12,9 +12,9 @@ | |||
* for example: A formatted text field in Drupal | |||
*/ | |||
#} | |||
{% set blockquote_base_class_var = blockquote_base_class|default('blockquote') %} | |||
{% set blockquote_base_class = blockquote_base_class|default('blockquote') %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same question here
<ol{{ bem(ol_base_class, (modifiers), blockname) }}> | ||
{% for ol_item in ol_items %} | ||
{% include "@atoms/03-lists/_list-item.twig" | ||
with { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if we've talked about this, but I think we should standardize on having the with {
not break to a new line. So this section would become:
{% include "@atoms/03-lists/_list-item.twig" with {
"list_item_label": ol_item.label,
"list_item_content": ol_item.content,
} %}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I like this and I think it's becoming pretty standard. Done!
@@ -1,7 +1,7 @@ | |||
button_url: | |||
"#" | |||
"#" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
four spaces
button_content: | ||
"Default Button" | ||
button_url: | ||
"#" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
four spaces
@@ -14,9 +14,9 @@ | |||
* for example: to insert an html5 video component | |||
*/ | |||
#} | |||
{% set video_base_class_var = video_base_class|default('video') %} | |||
{% set video_base_class = video_base_class|default('video') %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_var?
@@ -0,0 +1,42 @@ | |||
<?php |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to commit this file? Or will the install scripts thing put it here for us on npm install
or yarn install
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't going to commit it, but once it gets added with the install script, it gets added to the repo. And I wasn't sure whether gitignoring it was a good idea either. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will we ever update it? And if so, do we want people to be aware of the update?
I'm thinking:
If we won't update it, git ignore it
If we will update it and git ignore it, they won't know there's an update
If we will update it and they commit it, they'll see the update when they do a git diff
Not sure what's right, just laying out the possibilities I see
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, we added it to the install process because we thought we might update it at some point, so... ?
@@ -17,7 +17,7 @@ | |||
{% if title %} | |||
{% include "@atoms/02-text/00-headings/heading-1/heading-1.twig" | |||
with { | |||
"heading": title, | |||
"heading": title |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should keep the ,
on every line (including only lines, and last lines) so that PRs only show new lines of code and don't report this line as changed when we add a comma because we've added a second line.
Wow... that's kinda confusing... if we don't include the comma here, and then add a line to make it
"heading": title,
"heading_blockname": "awesome",
The diff will show two lines have changed (because of the comma) instead of just showing the one new line as a change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mentioned a bunch of stuff, so requesting changes
Thins have changed since this was approved...
@ModulesUnraveled I've made a lot of changes per your review, and if I didn't I tried to address them in comments. Can you take another look when you get a minute? Thank you! |
<blockquote{{ bem(blockquote_base_class, (modifiers), blockname) }}> | ||
<blockquote | ||
{{ bem(blockquote_base_class, (blockquote_modifiers), blockquote_blockname) }} | ||
> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine, but is a case where I'd probably just keep it inline with blockquote
since it's the only attribute we're defining
"heading": "This is an Example Page" | ||
}%} | ||
"heading": "This is an Example Page", | ||
"h1_modifiers": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why I didn't mention this before, but this can be written shorter, like this:
"h1_modifiers": ["margin"],
And I'll give another example of multiples below
|
||
{% include "@organisms/card-grid/card-grid.twig" with { | ||
"grid_blockname": "card", | ||
"grid_modifiers": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As promised, here's an example of how to write multiples in the array:
"grid_modifiers": ["divider", "columns-3"],
Fewer lines, easier to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One comment about where the example page file is placed. With that moved, this is as far as I'm concerned.
@@ -0,0 +1,94 @@ | |||
<div class="example-page"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized that this file is in the 04-templates/example-page
directory. I think it should be in the 05-pages
directory (and not in a sub-directory) since it contains actual data, and not "replacement blocks".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
Wouldn't it be lovely if instead of using different syntax for Drupal and Pattern Lab you could simply use this for adding classes
<div{{ bem('block') }}>
and it just magically worked?And technically, the syntax is:
<div{{ bem('block', (block_modifiers), block_blockname) }}>
.. with the first being the blockname, the second being modifiers, and the third being a blockname override (modifiers and blockname optional). 💯
Changes:
components/_twig-components/functions/bem.function.php
) to be able to pass in classes to Pattern Lab files (using simplyclass=""
) and Drupal files (using{{ attributes }}
) correctly.components/_patterns/01-atoms/02-text/00-headings/heading-1/heading-1.twig
andcomponents/_patterns/02-molecules/_block.twig
for an example usage of the newbem
function. The block twig file is used directly in Drupal and correctly adds the bem class to the existing Drupal attributesTesting instructions:
npm start
to run Pattern Lab and verify Header 1 is accurately printing classes from file and ymlcomponents/_patterns/02-molecules/_block.twig
as well as Drupal attributes.To-do: