The Drop Times: Best of Both Worlds: Thor Andre Gretland on Gutenberg and Drupal's Synergy

Discover the innovative journey of Drupal Gutenberg through the insights of Thor Andre Gretland, Head of Sales and Business Advisor at Frontkom. In an exclusive interview with The DropTimes, Thor Andre unveils how Gutenberg is revolutionizing the Drupal ecosystem, enhancing content creation, and bridging communities. Learn about the groundbreaking collaboration between WordPress and Drupal, the challenges addressed, and the future of open-source CMS development. From improving user experience to addressing digital marketing needs, this interview is a deep dive into the evolving world of content management.

Joachim's blog: Refactoring with Rector

Rector is a tool for making changes to PHP code, which powers tools that assist with upgrading deprecated code in Drupal. When I recently made some refactoring changes in Drupal Code Builder, which were too complex to do with search and replace regexes, it seemed like a good opportunity to experiment with Rector, and learn a bit more about it.

Besides, I'm an inveterate condiment-passer: I tend to prefer spending longer on a generalisation of a problem than on the problem itself, and the more dull the problem and the more interesting the generalisation, the more the probability increases.

So faced with a refactoring from this return from the getFileInfo() method:

return [ 'path' => '', 'filename' => $this->component_data['filename'], 'body' => [], 'merged' =>$merged, ];

to this:

return new CodeFile( body_pieces: $this->component_data['filename'], merged: $merged, );

which was going to be tedious as hell to do in a ton of files, obviously, I elected to spend time fiddling with Rector.

The first thing I'll say is that the same sort of approach as I use with migrations works well: work with a small indicative sample, and iterate small changes. With a migration, I will find a small number of source rows which represent different variations (or if there is too much variation, I'll iterate the iteration multiple times). I'll run the migration with just those sources, examine the result, make refinements to the migration, then roll back and repeat.

With Rector, you can specify just a single class in the code that registers the rule to RectorConfig in the rector.php file, so I picked a class which had very little code, as the dump() output of an entire PHP file's PhpParser analysis is enormous.

You then use the rule class's getNodeTypes() method to declare which node types you are interested in. Here, I made a mis-step at first. I wanted to replace Array_ nodes, but only in the getFileInfo() method. So in my first attempt, I specified ClassMethod nodes, and then in refactor() I wrote code to drill down into them to get the array Array_ nodes. This went well until I tried returning a new replacement node, and then Rector complained, and I realised the obvious thing I'd skipped over: the refactor() method expects you to return a node to replace the found node. So my approach was completely wrong.

I rewrote getNodeTypes() to search for Array_ nodes: those represent the creation of an array value. This felt more dangerous: arrays are defined in lots of places in my code! And I haven't been able to see a way to determine the parentage of a node: there do not appear to be pointers that go back up the PhpParser syntax tree (it would be handy, but would make the dump() output even worse to read!). Fortunately, the combination of array keys was unique in DrupalCodeBuilder, or at least I hoped it was fairly unique. So I wrote code to get a list of the array's keys, and then compare it to what was expected:

foreach ($node->items as $item) { $seen_array_keys[] = $item->key->value; } if (array_intersect(static::EXPECTED_MINIMUM_ARRAY_KEYS, $seen_array_keys) != static::EXPECTED_MINIMUM_ARRAY_KEYS) { return NULL; }

Returning NULL from refactor() means we aren't interested in this node and don't want to change it.

With the arrays that made it through the filter, I needed to make a new node that's a class instantiation, to replace the array, passing the same values to the new statement as the array keys (mostly).

Rector's list of commonly used PhpParser nodes was really useful here.

A new statement node is made thus:

use PhpParser\Node\Name; use PhpParser\Node\Expr\New_; $class = new Name('\DrupalCodeBuilder\File\CodeFile'); return new New_($class);

This doesn't have any parameters yet, but running Rector on this with my sample set showed me it was working properly. Rector has a dry run option for development, which shows you what would change but doesn't write anything to files, so you can run it over and over again. What's confusing is that it also has a cache; until I worked this out I was repeatedly confused by some runs having no effect and no output. I have honestly no idea what the point is of caching something that's designed to make changes, but there is an option to disable it. So the command to run is: $ vendor/bin/rector --dry-run --clear-cache. Over and over again.

Once that worked, I needed to convert array items to constructor parameters. Fortunately, the value from the array items work for parameters too:

use PhpParser\Node\Arg; foreach ($node->items as $array_item) { $construct_argument = new Arg( $array_item->value, );

That gave me the values. But I wanted named parameters for my constructor, partly because they're cool and mostly because the CodeFile class's __construct() has optional parameters, and using names makes that simpler.

Inspecting the Arg class's own constructor showed how to do this:

use PhpParser\Node\Arg; use PhpParser\Node\Identifier; $construct_argument = new Arg( value: $array_item->value, name: new Identifier($key), );

Using named parameters here too to make the code clearer to read!

It's also possible to copy over any inline comments that are above one node to a new node:

// Preserve comments. $construct_argument->setAttribute('comments', $array_item->getComments());

The constructor parameters are passed as a parameter to the New_ class:

return new New_($class, $new_call_args);

Once this was all working, I decided to do some more refactoring in the CodeFile class in DrupalCodeBuilder. The changes I was making with Rector made it more apparent that in a lot of cases, I was passing empty values. Also, the $body parameter wasn't well-named, as it's an array of pieces, so could do with a more descriptive name such as $body_pieces.

Changes like this are really easy to do (though by this point, I had made a git repository for my Rector rule, so I could make further enhancements without breaking what I'd got working already).

foreach ($node->items as $array_item) { $key = $array_item->key->value; // Rename the 'body' key. if ($key == 'body') { $key = 'body_pieces'; }

And that's my Rector rule done.

Although it's taken me far more time than changing each file by hand, it's been far more interesting, and I've learned a lot about how Rector works, which will be useful to me in the future. I can definitely see how it's a very useful tool even for refactoring a small codebase such as DrupalCodeBuilder, where a rule is only going to be used once. It might even prompt me to undertake some minor refactoring tasks I've been putting off because of how tedious they'll be.

What I've not figured out is how to extract namespaces from full class names to an import statement, or how to put line breaks in the new statement. I'm hoping that a pass through with PHP_CodeSniffer and Drupal Coder's rules will fix those. If not, there's always good old regexes!

Tags: refactoringRectorPhpParserdrupal code builder

ThinkDrop Consulting: Introducing Operations Site Runner: a self-hosted CI/CD platform using GitHub Actions and DDEV.

Introducing Operations Site Runner: a self-hosted CI/CD platform using GitHub Actions and DDEV. admin Fri, 05/03/2024 - 16:47

I've been building and designing automation systems for almost 20 years. I built DevShop on top of Aegir to implement continuous integration and quality control over 10 years ago.

Running CI systems is hard. Really hard. There needs to be an active task runner. A dashboard. API integrations. Tooling. Network Access. It can be incredibly complicated. In the Enterprise? Forget it.

I've been imagining a new system for many years, and here it is.

Drupal Association blog: 5 Unmissable Attractions to Explore Around DrupalCon Portland 2024

Portland, Oregon – the Rose City, home to an array of charming experiences that extend beyond the walls of this year's much-anticipated DrupalCon. While knowledge sharing and the industry buzz at the Oregon Convention Center will undoubtedly be the main draw, the locale offers a diversity of attractions, from serene parks to bustling markets. For those fortunate enough to attend DrupalCon, it would be a miss not to maximize your time and immerse yourself in the unique culture Portland has to offer. Here are five more local attractions, in addition to our previous recommendations, that promise to enrich your DrupalCon experience and provide unforgettable memories.

1. Cruise the City on E-Scooters around Peace Memorial Park

SW corner of NE Oregon St and, NE Lloyd Blvd, Portland, OR 97232

Arriving in Portland, the first thing visitors often notice is the city's commitment to sustainability and the vibrant outdoor lifestyle. What better way to experience this than by gliding through the renowned bike paths and urban green gardens on an E-Scooter? A stone's throw away from the Oregon Convention Center, Peace Memorial Park provides a picturesque setting that is perfect for a leisurely ride. With the Willamette River flowing alongside and the skyscrapers beyond the riverbank, this sanctuary of serenity is a stark contrast to the bustling city center.

2. Discover the charm of Portland’s most historic rose garden

400 SW Kingston Ave, Portland, OR 97205, United States

Known as the City of Roses, Portland proudly hosts the International Rose Test Garden, the oldest of its kind in the United States that has been in continuous operation. With the arrival of spring, there's no better moment to witness the garden's vibrant first blooms. Showcasing over 10,000 roses across 610 varieties, the garden not only offers a breathtaking display but also serves a crucial role in the cultivation and testing of new rose species. As a sanctuary for hybrid roses from across the globe, the garden continues its legacy of innovation and preservation in the heart of Portland.

3. Savor Artisanal Coffee at Roseline Coffee Cafe & Roastery

321 NE Davis St, Portland, OR 97232

Portland is known for its craft coffee culture, and Roseline Coffee Cafe & Roastery stands as a testament to this. Just moments from the convention center, this local favorite offers a welcoming reprieve from the conference crowds. Here, you can try blends and single-origin roasts that represent the pinnacle of Portland's coffee craft. Whether you’re an espresso aficionado or simply in need of a caffeine hit, the experience at Roseline will elevate your DrupalCon visit.

4. Explore Exhibitions At The Portland Art Museum

1219 SW Park Ave, Portland, OR 97205

Just a brief drive from the Oregon Convention Center, the Portland Art Museum stands as Oregon's largest and one of the nation's oldest art institutions. Nestled within two historic buildings in Portland’s South Park Blocks, a key part of the city's cultural district, the museum boasts an extensive and diverse art collection. Visitors can purchase Portland Art Museum tickets online or at the museum, with adult admission priced at $25. The Museum offers a wide array of exhibitions, from in-depth retrospectives of individual artists to comprehensive historical surveys and significant traveling exhibitions from across the globe. These exhibitions showcase pieces from the museum's own collection alongside masterpieces loaned from other museums and private collections worldwide.

5. Immerse Yourself in the Quirkiness of the Portland Saturday Market

2 SW Naito Pkwy, Portland, OR 97204

If your stay in Portland includes the weekend, the Portland Saturday Market offers a vibrant immersion into the local eccentricity and artisanal zeal that define the City of Roses.

A visit to this lively gathering can be enriching and is just a short drive away from the Oregon Convention Center. Wandering through the maze of stalls, you’ll find an array of handcrafted delights – from jewelry to leather goods, pottery to fine art – all lovingly crafted by the city’s talented makers. The sounds of live music and the aroma of delectable local cuisine will captivate your senses, while the palpable sense of community will remind you of the inclusive spirit that saturates Portland's identity. Whether you're making a purchase or simply taking in the scene, the Saturday Market encapsulates the heart and soul of the city, making it a must-visit destination.

---

With these five enriching experiences, your DrupalCon excursion will extend far beyond the convention doors. You'll build lasting connections with both the Drupal community and the diverse tapestry of Portland. Whether you're charting a solo adventure or teaming up with fellow tech enthusiasts, these local highlights are poised to enhance your trip with a delightful blend of tranquility, creativity, and community.

The Drop Times: Exclusive Insights from Keynote Speakers of DrupalCon Portland 2024

Step behind the curtain of DrupalCon Portland's keynote lineup and immerse yourself in a world of innovation and expertise. Join us as we unveil exclusive insights from industry leaders, including Cristina Chumillas, Janez Urevc, Ted Bowman, Fran Garcia-Linares, Jürgen Haas, and Mateu Aguiló Bosch, offering a tantalizing glimpse into the transformative sessions awaiting attendees

Drupal Core News: Announcing the inaugural Project Update Working Group members

Congratulations to the inaugural members of the new Project Update Working Group.

This is a new working group tasked with helping maintainers prepare contributed projects for the next major release of Drupal core.

The inaugural members are as follows:

  1. Norah Medlin (tekNorah) (provisional)

  2. Vladimir Roudakov (vladimiraus)

  3. Sven Decabooter (svendecabooter)

  4. Naveen Valecha (naveenvalecha)

  5. Kristen Pol (Kristen Pol)

  6. Matt Glaman (mglaman)

  7. Darren Oh (Darren Oh)

  8. Mark Casias (markie)

  9. Kim Pepper (kim.pepper)

  10. Björn Brala (bbrala)

  11. Lucas Hedding (heddn)

  12. Pedro Cambra (pcambra)

  13. Allan Chappell (generalredneck)

  14. Jakob Perry (japerry)

  15. Timo Huisman (Timo Huisman) (provisional)

The group will work in the coming weeks to establish processes and changes required to Drupal.org to facilitate the role.

If you wish to get in touch and say congratulations, you can find them in the #project-update-working-group channel on slack.

Four Kitchens: Aligning diverging websites with an upstream platform through Drupal

Image removed.

Mike Goulding

Senior Drupal Engineer

Mike has been part of the Four Kitchens crew since 2018, where he works as a senior engineer and tech lead for a variety of Drupal and WordPress projects.

January 1, 1970

Image removed.The Columbia SPS homepage

For higher ed institutions, the need to manage updates for multiple websites is an ongoing struggle. Each site should reflect the distinct identity of a given school or department. But as each website’s CMS, frontend design, and backend architecture diverge, any organization will need to grapple with the necessary upkeep.

Columbia School of Professional Studies (SPS) faced this situation with three separate websites. One site presents summer courses, another targets high school students with pre-college offerings, and the third is the traditional SPS website. Each domain serves a different audience and is managed by a small team.

As each website continued to diverge, users found it difficult to recognize them as part of the same school. Worse yet, the three websites were on two different versions of Drupal and had grown difficult to maintain, as one platform was reaching its end of life.

SPS came to Four Kitchens seeking an upstream solution to provide relief. In this preview of an upcoming presentation at DrupalCon Portland 2024’s Higher Education Summit, the Config Split module has a newer feature that cleared the way for an efficient resolution.

How an upstream platform streamlines diverging websites

Columbia SPS needed a solution that would resolve multiple nagging issues. In much the same way that a design system streamlines operations by creating a centralized content resource, an upstream platform enables multiple websites to share a single codebase.

Along with bringing the organization’s Drupal instances into alignment and reducing technical debt, the approach offered three core advantages:

  • Increased efficiency: Enable the university to maintain multiple websites with less effort. When you update code in one place, it impacts every site in the organization.
  • Greater consistency: Align user experience and simplify internal planning for site updates.
  • Streamlined development: Shared code, security updates, and component access. No matter what site Columbia’s team works on, they know what processes to expect.

To make sure each site could still offer a distinct experience, Columbia didn’t want to share content or merge each website into one. They primarily wanted to make each easier to manage for their team.

Offer shared (but distinct) experiences through Config Split

Creating an upstream platform for Columbia SPS hinged on the Configuration Split Drupal module. Put simply, this module allows you to apply a different configuration to a website to suit specific scenarios. You can use Config Split to make sure you only see error logs on your test environment (not your live site).

Image removed.The Columbia SPS Summer Session website

However, Columbia SPS still wanted its three websites to offer distinct features. To enable this flexibility, we used a newer feature in the 2.x versions of the module called Config Patch. This feature allows Columbia SPS to apply part of a website configuration to each website.

For example, each university website may share the same article structure. But one website can support a distinctive CTA component at the bottom. Columbia SPS now has that flexibility — and it doesn’t cause chaos from a website maintenance standpoint.

With Config Patch, Columbia can use a single code repository to maintain three sites that have their own distinct details within the same baseline features. We also provided SPS with a base demo site that keeps Config Split from allowing too much flexibility. Adding rules to settings.php provides a home for the logic for each site to make sure they follow the proper configuration.

Plus, the demo site functions as a mold if the organization needs to add a new website. Along with providing support for the organization’s current needs, the upstream platform provides support for the future.

Avoiding pitfalls of upstream platforms in higher ed

Implementing an upstream solution for Columbia SPS enabled the university to run its separate sites more efficiently and provide a more consistent experience. Just as importantly, the institution escaped the shadow of a Drupal 7 migration, which stands as a major benefit for the organization.

However, adopting an upstream platform carries its own complications. For all the advantages Columbia SPS gained, the organization also needs to be mindful of a few potential pitfalls of an upstream platform:

  1. Bringing distinct site features back into alignment is difficult: If Columbia SPS wanted to roll back a configuration that was previously split, the sites can be difficult to manage locally.
  2. Shifting priorities for Drupal updates: Platform updates must be made against the demo site first to maintain alignment between each web property.
  3. Increased work for developing multiple features: An upstream platform reduces complexity, but working in a single repository presents its own challenges. Creating distinct features for individual websites requires a little more work on the part of your development team.

Upstream platforms offer efficiency and consistency for higher ed

Navigating the specific needs for multiple websites is a persistent challenge for higher ed institutions. On the one hand, delivering a consistent experience drawn from a single codebase is easier to manage, especially for a small, centralized IT team. On the other hand, individual departments and schools have specific design and functionality needs. They should be able to offer website experiences distinct from the look and feel of your core website.

With an upstream platform, you gain the functionality to serve both needs. The solution introduces new complexity, but with an experienced development partner, a multisite platform allows your team to work more efficiently. Better still, if your organization needs to maintain multiple platforms as your websites have diverged, you gain key benefits from addressing needed upgrades.

Would this kind of solution help your organization? Let us know how we can help.

Details

If you’re going to DrupalCon Portland 2024, please make sure to attend the Higher Education Summit to hear directly from Mike and the team at Columbia SPS.

Where: Oregon Convention Center (777 NE Martin Luther King Jr. Blvd, Portland, OR 97232), Room C123-124

When: Thursday, May 9, 2024, 9:00am – 4:00pm

For tickets and session details, click here.

The post Aligning diverging websites with an upstream platform through Drupal appeared first on Four Kitchens.

Drupal Core News: The new Navigation module and Layout Builder

Navigation module makes use of Layout Builder to construct the navigation toolbar.

There have been some questions about this decision in Slack. This post discusses the background.

In #3397058: Convert navigation sections to blocks and use the menu system the navigation module added a plugin system and config entity for 'navigation blocks'. These were very nearly identical to block plugins and block config entities. The primary difference was the config entities did not depend on a theme like block config entities do.

In #3411099: Create an administration UI for managing Navigation Blocks a UI was added for editing and managing navigation blocks. This duplicated further code from the block module.

#3438895: Add the new Navigation to core as an Experimental module was the issue to add the navigation module to core. This point was the first time that many core committers had looked at the code. As part of a Framework Manager review of the issue, the amount of duplication between the block and navigation modules was raised.

Until this point the navigation module lived in contrib and did not have a chance to change code in core. But now that it was a merge request against core, changing core was a possibility. As a result the Framework Managers made an attempt to modify the theme-assumption in the block system to support the navigation use-case. This resulted in a less than ideal scenario where Block::getTheme() could return null or an empty string in some scenarios. Whilst it was possible to fix all calls in core, the impact this could have on contrib and custom code felt like it would be problematic.

At this point the idea of using Layout Builder's section storage as a data-model for the blocks in navigation was floated. Layout Builder's section storage provides a data-model that allows placing and configuring block plugins but without block config entities. There is no dependency between these block placements and a theme. Layout Builder also includes an API for limiting which blocks can be used where, which was also a requirement for the navigation module. Not all block plugins would work inside the navigation toolbar.

The Framework Managers worked on the core merge request to assess the feasibility of this change. The net result was the removal of 4,000 lines of code but with largely the same functionality. As a result, the version of the navigation module that was committed to Drupal 10.3.0 and 11.x depends on the Layout Builder module.

Sites who don't use Layout Builder for building entity displays can continue to use their preferred approach. Having Layout Builder enabled doesn't change how entities are rendered unless you enable it on a per entity-bundle-display basis. Prior to 10.3 there were performance issues from the number of block plugins derived by Layout Builder. But from 10.3 sites can now control and prevent this.

The Navigation initiative has created a list of follow-up issues for the usability of Layout Builder when configuring the navigation toolbar. Some of these overlap with existing usability issues for Layout Builder. In addition the recently announced Experience builder initiative will invest in improving Layout Builder usability. Finally, the Dashboard initiative is also using Layout Builder for handling block placements. When we standardise on a solution and work together to improve it, it will lead to improvement across the board.

Mario Hernandez: Integrating Drupal with Storybook components

Hey you're back! 🙂 In the previous post we talked about how to build a custom Drupal theme using Storybook as the design system. We also built a simple component to demonstrate how Storybook, using custom extensions, can understand Twig. In this post, the focus will be on making Drupal aware of those components by connecting Drupal to Storybook.
If you are following along, we will continue where we left off to take advantage of all the prep work we did in the previous post. Topics we will cover in this post include:

  1. What is Drupal integration
  2. Installing and preparing Drupal for integration
  3. Building components in Storybook
  4. Building a basic front-end workflow
  5. Integrating Drupal with Storybook components

What is Drupal integration?

In the context of Drupal development using the component-driven methodology, Drupal integration means connecting Drupal presenter templates such as node.html.twig, block.html.twig, paragraph.html.twig, etc. to Storybook by mapping Drupal fields to component fields in Storybook. This in turn allows for your Drupal content to be rendered wrapped in the Storybook components.

The advantage of using a design system like Storybook is that you are in full control of the markup when building components, as a result your website is more semantic, accessible, and easier to maintain.

Building more components in Storybook

The title component we built in the previous post may not be enough to demonstrate some of the advanced techniques when integrating components. We will build a larger component to put these techniques in practice. The component we will build is called Card and it looks like this:

Image removed.

When building components, I like to take inventory of the different parts that make up the components I'm building. The card image above shows three parts: An image, a title, and teaser text. Each of these parts translates into fields when I am defining the data structure for the component or building the entity in Drupal.

Building the Card component

  • Open the Drupal site in your code editor and within your code editor navigate to the storybook theme (web/themes/custom/storybook)
  • Create two new directories inside components called 01-atoms and 02-molecules
  • Inside 02-molecules create a new directory called card
  • Inside the card directory add the following four files:
    • card.css: component's styles
    • card.twig: component's markup and logic
    • card.stories.jsx: Storybook's story
    • card.yml: component's demo data
  • Add the following code snippet to card.yml:
--- modifier: '' image: <img src="https://source.unsplash.com/cHRDevKFDBw/640x360" alt="Palm trees near city buildings" /> title: level: 2 modifier: '' text: 'Tours & Experiences' url: 'https://mariohernandez.io' teaser: 'Step inside for a tour. We offer a variety of tours and experiences to explore the building’s architecture, take you backstage, and uncover the best food and drink. Tours are offered in different languages and for different levels of mobility.'
  • Add the following to card.twig to provide the markup and logic for the card:
{{ attach_library('storybook/card') }} <article class="card{{ modifier ? ' ' ~ modifier }}{{- attributes ? ' ' ~ attributes.class -}}" {{- attributes ? attributes|without(class) -}}> {% if image %} <div class="card__image"> <figure> {{ image }} </figure> </div> {% endif %} <div class="card__content"> {% if title %} {% include "@atoms/title/title.twig" with { 'level': title.level, 'modifier': title.modifier, 'text': title.text, 'url': title.url, } only %} {% endif %} {% if teaser %} <p class="card__teaser">{{ teaser }}</p> {% endif %} </div> </article>

Code snippet for building card

  • Copy and paste these styles into card.css.

  • Finally, let's create the Storybook card story by adding the following to card.stories.jsx:

import parse from 'html-react-parser'; import card from './card.twig'; import data from './card.yml'; import './card.css'; const component = { title: 'Molecules/Card', }; export const Card = { render: (args) => parse(card(args)), args: { ...data }, }; export default component;

Let's go over a few things regarding the code above:

  • The data structure in card.yml reflects the data structure and type we will use in Drupal.
    • The image field uses the entire <img> element rather than just using the image src and alt attributes. The reason for this is so when we get to Drupal, we can use Drupal's full image entity. This is a good practice for caching purposes.
  • card.twig reuses the title component we created in the previous post. Rather than build a title from scratch for the Card and repeat the code we already wrote, reusing the existing components keeps us DRY.
  • card.stories.jsx in the Storybook story for the Card, notice how the code in this file is very similar to the code in the title.stories.jsx. Even with complex components, when we port them into Storybook as stories, most times the code will be similar as what you see above because Storybook is simply parsing whatever is in .twig and .yml files. There are exceptions when the React code may have extra parameters or logic which typically happens when we're building stories variations. Maybe a topic for a different blog post. 😉

Before we preview the Card, some updates are needed

You may have noticed in card.twig we used the namespace @atoms when nesting the title component. This namespace does not exist, and we need to create it now. In addition, we need to move the title component into the 01-atoms directory:

  • In your code editor or command line (whichever is easier), move the title directory into the 01-atoms directory
  • In your editor, open title.stories.jsx and change the line
    title: 'Components/Title' to title: 'Atoms/Title'. This will display the title component within the Atoms category in Storybook's sidebar.
  • Rather than have you make individual changes to vite.config.js, let's replace/overwrite all its content with the following:
/* eslint-disable */ import { defineConfig } from 'vite' import yml from '@modyfi/vite-plugin-yaml'; import twig from 'vite-plugin-twig-drupal'; import { join } from 'node:path' export default defineConfig({ root: 'src', publicDir: 'public', build: { emptyOutDir: true, outDir: '../dist', rollupOptions: { input: { 'reset': './src/css/reset.css', 'styles': './src/css/styles.css', 'card': './src/components/02-molecules/card/card.css', }, output: { assetFileNames: 'css/[name].css', }, }, sourcemap: true, }, plugins: [ twig({ namespaces: { atoms: join(__dirname, './src/components/01-atoms'), molecules: join(__dirname, './src/components/02-molecules'), }, }), // Allows Storybook to read data from YAML files. yml(), ], })

Let's go over some of the most noticeable updates inside vite.config.js:

  • We have defined a few things to improve the functionality of our Vite project, starting with using src as our app root directory and public for publicDir. This helps the app understand the project structure in a relative manner.

  • Next, we defined a Build task which provides the app with defaults for things like where should it compiled code to (i.e. /dist), and rollupOptions for instructing the app which stylesheets to compile and what to call them.

  • As part of the rollupOptions we also defined two stylesheets for global styles (reset.css and styles.css). We'll create these next.

    Important

    This is as basic as it gets for a build workflow and in no way would I recommend this be your front-end build workflow. When working on bigger projects with more components, it is best to define a more robust and dynamic workflow that provides automation for all the repetitive tasks performed on a typical front-end project.
  • Under the Plugins section, we have defined two new namespaces, @atoms and @molecules, each of which points to specific path within our components directory. These are the namespaces Storybook understands when nesting components. You can have as many namespaces as needed.

Adding global styles

  • Inside storybook/src, create a new directory called css
  • Inside the css directory, add two new files, reset.css and styles.css
  • Here are the styles for reset.css and styles.css. Please copy them and paste them into each of the stylesheets.
  • Now for Storybook to use reset.css and styles.css, we need to update /.storybook/preview.js by adding these two imports directly after the current imports, around line 4.
import '../dist/css/reset.css'; import '../dist/css/styles.css';

Previewing the Card in Storybook

Remember, you need NodeJS v20 or higher as well as NVM installed on your machine
  • In your command line, navigate to the storybook directory and run:
nvm install npm install npm run build npm run storybook

A quick note about the commands above:

  • nvm install and npm install are typically only done once in your app. These commands will first install and use the node version specified in .nvmrc, and will install all the required node packages found in package.json. If you happen to be workign on another project that may use a different version of node, when you comeback to the Storybook project you will need to run nvm use in order to resume using the right node version.
  • npm run build is usually only ran when you have made configuration changes to the project or are introducing new files.
  • npm run storybook is the command you will use all the time when you want to run Storybook.

After Storybook launches, you should see two story categories in Storybook's sidebar, Atoms and Molecules. The title component should be under Atoms and the Card under Molecules. See below:

Image removed.

Installing Drupal and setting up the Storybook theme

We have completed all the prep work in Storybook and our attention now will be all in Drupal. In the previous post all the work we did was in a standalone project which did not require Drupal to run. In this post, we need a Drupal site to be able to do the integration with Storybook. If you are following along and already have a Drupal 10 site ready, you can skip the first step below.

  1. Build a basic Drupal 10 website (I recommend using DDEV).
  2. Add the storybook theme to your website. If you completed the excercise in the previous post, you can copy the theme you built into your site's /themes/custom/ directory, Otherwise, you can clone the previous post repo into the same location so it becomes your theme. After this your theme's path should be themes/custom/storybook.
  3. No need to enable the theme just yet, we'll come back to the theme shortly.
  4. Finally, create a new Article post that includes a title, body content and an image. We'll use this article later in the process.

Creating Drupal namespaces and adding Libraries

Earlier we created namespaces for Storybook, now we will do the same but this time for Drupal. It is best if the namesapces' names between Storybook and Drupal match for consistency. In addition, we will create Drupal libraries to allow Drupal to use the CSS we've written.

  • Install and enable the Components module
  • Add the following namespaces at the end of storybook.info.yml (mind your indentation):
components: namespaces: atoms: src/components/01-atoms molecules: src/components/02-molecules
  • Replace all content in storybook.libraries.yml with the following:
global: version: VERSION css: base: dist/css/reset.css: {} dist/css/styles.css: {} card: css: component: dist/css/card.css: {}
  • Let's go over the changes to both, storybook.info.yml and storybook.libraries.yml files:

    • Using the Components module we created two namespaces: @atoms and @molecules. Each namespace is associated with a specific path to the corresponding components. This is important because Drupal by default only looks for Twig templates inside the /templates directory and without the Components module and the namespaces it would not know to look for our component's Twig templates inside the components directory.
    • Then we created two Drupal libraries: global and card. The Global library includes two CSS stylesheets (reset.css and styles.css), which handle base styles in our theme. the Card library includes the styles we wrote for the Card component. If you noticed, when we created the Card component, the first line inside card.twig is a Twig attach library statement. Basically card.twig is expecting a Drupal library called card.

Turn Twig debugging on

All the pieces are in place to Integrate the Card component so Drupal can use it to render article nodes when viewed in teaser view mode.

  • The first thing we need to do to begin the integration process is to determine which Twig template Drupal uses to render article nodes in teaser view mode. One easy way to do this is by turning Twig debugging on. This used to be a complex configuration but starting with Drupal 10.1 you can now do it directly in Drupal's UI:

    • While logged in with admin access, navigate to /admin/config/development/settings on your browser. This will bring up the Development settings page.
    • Check all the boxes on this page and click Save settings. This will enable Twig debugging and disable caching.
    • Now navigate to /admin/config/development/performance so we can turn CSS and JS aggregation off.
    • Under Bandwidth optimization cleared the two boxes for CSS and Javascript aggregation then click on Save configuration.
    • Lastly, click the Clear all caches button. This will ensure any CSS or JS we write will be available without having to clear caches.
  • With Twig debugging on, go to the homepage where the Article we created should be displayed in teaser mode. If you right-click on any part of the article and select inspect from the context menu, you will see in detail all the templates Drupal is using to render the content on the current page. See example below.

    Note

    I am using a new basic Drupal site with Olivero as the default theme. If your homepage does not display Article nodes in teaser view mode, you could create a simple Drupal view to list Article nodes in teaser view mode to follow along.

Image removed.

In the example above, we see a list of templates that start with node...*. These are called template suggestions and are the names Drupal is suggesting we can assign our custom templates. The higher the template appears on the list, the more specific it is to the piece of content being rendered. For example, changes made to node.html.twig would affect ALL nodes throughout the site, whereas changes made to node--1--teaser.html.twig will only affect the first node created on the site but only when it's viewed in teaser view mode.

Notice I marked the template name Drupal is using to render the Article node. We know this is the template because it has an X before the template name.

In addition, I also marked the template path. As you can see the current template is located in core/themes/olivero/templates/content/node--teaser.html.twig.

And finally, I marked examples of attributes Drupal is injecting in the markup. These attributes may not always be useful but it is a good practice to ensure they are available even when we are writing custom markup for our components.

Create a template suggestion

By looking at the path of the template in the code inspector, we can see that the original template being used is located inside the Olivero core theme. The debugging screenshot above shows a pretty extensive list of templates suggestions, and based on our requirements, copying the file node--teaser.html.twig makes sense since we are going to be working with a node in teaser view mode.

  • Copy /core/themes/olivero/templates/content/node--teaser.html.twig into your theme's /storybook/templates/content/. Create the directory if it does not exist.
  • Now rename the newly copied template to node--article--teaser.html.twig.
  • Clear Drupal's cache since we are introducing a new Twig template.

As you can see, by renaming the template node--article--teaser (one of the names listed as a suggestion), we are indicating that any changes we make to this template will only affect nodes of type Article which are displayed in Teaser view mode. So whenever an Article node is displayed, if it is in teaser view mode, it will use the Card component to render it.

The template has a lot of information that may or may not be needed when integrating it with Storybook. If you recall, the Card component we built was made up of three parts: an image, a title, and teaser text. Each of those are Drupal fields and these are the only fields we care about when integrating. Whenever when I copy a template from Drupal core or a module into my theme, I like to keep the comments on the template untouched. This is helpful in case I need to reference any variables or elements of the template.

The actual integration ...Finally

  1. Delete everything from the newly copied template except the comments and the classes array variable
  2. At the bottom of what is left in the template add the following code snippet:
{% set render_content = content|render %} {% set article_title = { 'level': 2, 'modifier': 'card__title', 'text': label, 'url': url, } %} {% include '@molecules/card/card.twig' with { 'attributes': attributes.addClass(classes), 'image': content.field_image, 'title': article_title, 'teaser': content.body, } only %}
  • We set a variable with content|render as its value. The only purpose for this variable is to make Drupal aware of the entire content array for caching purposes. More info here.
  • Next, we setup a variable called article_title which we structured the same way as data inside card.yml. Having similar data structures between Drupal and our components provides many advantages during the integration process.
    • Notice how for the text and url properties we are using Drupal specific variables (label and url), accordingly. If you look in the comments in node--article--teaser.html.twig you will see these two variables.
  • We are using a Twig include statement with the @molecules namespace to nest the Card component into the node template. The same way we nested the Title component into the Card.
  • We mapped Drupal's attributes into the component's attributes placeholder so Drupal can inject any attributes such as CSS classes, IDs, Data attributes, etc. into the component.
  • Finally, we mapped the image, title and teaser fields from Drupal to the component's equivalent fields.
  • Save the changes to the template and clear Drupal's cache.

Enable the Storybook theme

Before we forget, let's enable the Storybook theme an also make it your default theme, otherwise all the work we are doing will not be visible since we are currently using Olivero as the default theme. Clear caches after this is done.

Previewing the Article node as a Card

Integration is done and we switched our default theme to Storybook. After clearing caches if you reload the homepage you should be able to see the Article node you wrote but this time displayed as a card. See below:
Image removed.

  • If you right-click on the article and select Inspect, you will notice the following:
    • Drupal is now using node--article--teaser.html.twig. This is the template we created.
    • The template path is now themes/custom/storybook/src/templates/content/.
    • You will also notice that the article is using the custom markup we wrote for the Card component which is more semantic, accessible, but in addition to this, the <article> tag is also inheriting several other attributes that were provided by Drupal through its Attributes variable. See below:

Image removed.

If your card's image size or aspect ratio does not look as the one in Storybook, this is probably due to the image style being used in the Article Teaser view mode. You can address this by:

  • Going to the Manage display tab of the Article's Teaser view mode (/admin/structure/types/manage/article/display/teaser).
  • Changing the image style for the Image field for one that may work better for your image.
  • Preview the article again on the homepage to see if this looks better.

In closing

This is only a small example of how to build a simple component in Storybook using Twig and then integrate it with Drupal, so content is rendered in a more semantic and accessible manner. There are many more advantages of implementing a system like this. I hope this was helpful and see the potential of a component-driven environment using Storybook. Thanks for visiting.

Download the code

For a full copy of the code base which includes the work in this and the previous post, clone or download the repo and switch to the card branch. The main branch only includes the previous post code.

Download the code

Drupal.org blog: Best Drupalcon Portland 2024 sessions to learn Drupal for the first time

I have gone through all the Drupalcon sessions in Portland and selected those that I think are perfect for someone learning Drupal, here is the result.

Did I miss any that you think it should be highlighted here? Please let me know 😊.

Have fun, learn and meet the community

Trivia night
https://events.drupal.org/portland2024/session/trivia-night
When: Thursday, May 9, 2024 - 18:30 to 21:05
Why: General culture about Drupal

Birds of a Feather
https://events.drupal.org/portland2024/session/birds-feather 
When: Monday, May 6, 2024 - 08:00 to 17:00
Why: Learn and interact with the discussions

Learn how Drupal is used in the real world 

Harvard College: Don't Call it a Redesign
https://events.drupal.org/portland2024/session/harvard-college-dont-call-it-redesign 
When: Thursday, May 9, 2024 - 14:20 to 14:45
Why: learn about current trends and on going work from real agencies in the real world

Creating Nimble Drupal Systems for Government: Transforming MN’s Dept of Health in 6 Months
https://events.drupal.org/portland2024/session/creating-nimble-drupal-systems-government-transforming-mns-dept-health-6 
When: Thursday, May 9, 2024 - 13:10 to 13:45
Why: Learn about current trends and on going work from real agencies in the real world

How Los Angeles Department of Water and Power Revolutionized the Web Utility Experience
https://events.drupal.org/portland2024/session/how-los-angeles-department-water-and-power-revolutionized-web-utility 
When: Thursday, May 9, 2024 - 11:30 to 11:45
Why: Learn about current trends and on going work from real agencies in the real world

How Drupal Rescued Georgia Tech’s International Students During and Post-Pandemic
https://events.drupal.org/portland2024/session/how-drupal-rescued-georgia-techs-international-students-during-and-post
When: Thursday, May 9, 2024 - 11:30 to 11:45
Why: Learn about current trends and on going work from real agencies in the real world

How Acquia and Drupal Power Robust, Modern, and Secure Digital Experiences
https://events.drupal.org/portland2024/session/how-acquia-and-drupal-power-robust-modern-and-secure-digital-experiences
When: Thursday, May 9, 2024 - 11:00 to 11:25
Why: Learn about current trends and on going work from real agencies in the real world

From Many to One: Migrating 70+ Disparate Local Government Websites onto a Cohesive Drupal Platform
https://events.drupal.org/portland2024/session/many-one-migrating-70-disparate-local-government-websites-cohesive-drupal 
When: Monday, May 6, 2024 - 13:30 to 14:20
Why: Learn about Drupal’s multisite capabilities

Get trained

TRAINING | Evolving Web
https://events.drupal.org/portland2024/session/training-debug-academy 
When: Thursday, May 9, 2024 - 09:00 to 16:00
Why: Introduction to Building Sites with Drupal

TRAINING | Evolving Web
https://events.drupal.org/portland2024/session/training-evolving-web 
When: Thursday, May 9, 2024 - 09:00 to 16:00
Why: Drupal Theming with SDC and TailwindCSS

First-time contributor workshop
https://events.drupal.org/portland2024/session/first-time-contributor-workshop 
When: Wednesday, May 8, 2024 - 10:30 to 17:00
Why: Learn to give something back while you learn something new

General Contribution
https://events.drupal.org/portland2024/session/general-contribution 
When: Monday, May 6, 2024 - 16:10 to 17:00
Why: It’s not necessarily a place to get trained, but a place where you can start contributing while volunteers will help you on how to do it.

Learn about Drupal capabilities

Access Control Strategies for Enterprise Drupal Websites
https://events.drupal.org/portland2024/session/access-control-strategies-enterprise-drupal-websites 
When: Tuesday, May 7, 2024 - 16:10 to 17:00
Why: Learn how the powerful Drupal access control works

Using Layout Builder: Practical Advice from the Field
https://events.drupal.org/portland2024/session/using-layout-builder-practical-advice-field 
When: Tuesday, May 7, 2024 - 15:00 to 15:50
Why: Learn about the powerful Layout Builder

Protecting your site with Automatic Updates
https://events.drupal.org/portland2024/session/protecting-your-site-automatic-updates 
When: Tuesday, May 7, 2024 - 15:00 to 15:50
Why: Learn to stay secure

Secure, Performant, Scalable and Green: The big wins of a static Drupal website
https://events.drupal.org/portland2024/session/secure-performant-scalable-and-green-big-wins-static-drupal-website 
When: Tuesday, May 7, 2024 - 15:00 to 15:50
Why: Learn to build static websites while leveraging the power of Drupal 

Unleashing the power of ECA: No-code coding for ambitious site builders
https://events.drupal.org/portland2024/session/unleashing-power-eca-no-code-coding-ambitious-site-builders 
When: Tuesday, May 7, 2024 - 13:50 to 14:40
Why: Learn some low code capabilities in Drupal

Learn about teamwork and collaboration
https://events.drupal.org/portland2024/session/price-silence-hidden-costs-withholding-feedback-teams 
When: Tuesday, May 7, 2024 - 16:10 to 17:00
Why: Because teamwork is the name of the game

Getting started using Personalization
https://events.drupal.org/portland2024/session/getting-started-using-personalization
When: Tuesday, May 7, 2024 - 11:30 to 12:20
Why: I personally believe that personalisation is the next big thing in Drupal and the web

Navigation changes in Drupal’s Admin UI
https://events.drupal.org/portland2024/session/navigation-changes-drupals-admin-ui 
When: Monday, May 6, 2024 - 15:00 to 15:50
Why: Learn about the new navigation interface 

Drupal's next leap: configuration validation — it's here!
https://events.drupal.org/portland2024/session/drupals-next-leap-configuration-validation-its-here 
When: Monday, May 6, 2024 - 15:00 to 15:50
Why:  Configuration is a powerful but complex topic in Drupal worth to explore

Lightening Talk: 5 new free things you get from CKEditor 5 Plugin Pack
https://events.drupal.org/portland2024/session/lightening-talk-5-new-free-things-you-get-ckeditor-5-plugin-pack 
When: Monday, May 6, 2024 - 13:05 to 13:15
Why: Learn more about the editor in the core of the Drupal editorial experience

Mastering Consistency: Expanding content across multiple sites and touch points
https://events.drupal.org/portland2024/session/mastering-consistency-expanding-content-across-multiple-sites-and-touch-points 
When: Monday, May 6, 2024 - 09:00 to 09:50
Why: Learn how flexible is Drupal when it comes to content shareability

Learn strategy and where Drupal is heading

Drupal Project Initiatives Keynote
https://events.drupal.org/portland2024/session/drupal-project-initiatives-keynote 
When: Wednesday, May 8, 2024 - 09:00 to 10:00
Why: Learn about Drupal future

Lightning Talk: Is the Redesign Dead?
https://events.drupal.org/portland2024/session/lightening-talk-redesign-dead
When: Monday, May 6, 2024 - 15:55 to 16:05
Why: Learn new trends in development

Drupal.org Update
https://events.drupal.org/portland2024/session/drupalorg-update 
When: Monday, May 6, 2024 - 15:00 to 15:50
Why: The engineering team will give you insights on what’s happening and what’s coming soon

So I logged in, now what? The Dashboard initiative welcomes you
https://events.drupal.org/portland2024/session/so-i-logged-now-what-dashboard-initiative-welcomes-you 
Monday, May 6, 2024 - 13:30 to 14:20
Why: learn how the new interface will welcome you in the near future.

Driesnote
https://events.drupal.org/portland2024/session/driesnote 
When: Monday, May 6, 2024 - 10:45 to 11:45
Why: Do we need to explain why the most important session in Drupalcon will give you insights in the immediate future of Drupal?