Problem/Motivation
SDC automatically add a \Drupal\Core\Template\Attribute attributes
prop in ComponentsTwigExtension::mergeAdditionalRenderContext.
This prop can be used as any "normal" prop:
$element['#props']['attributes'] = $value;
Some existing Drupal mechanisms, in Core & Contrib, expect and leverage this prop. For example, |add_class() Twig filter:
public function addClass(array $element, ...$classes): array {
$attributes = new Attribute($element['#attributes'] ?? []);
$attributes->addClass(...$classes);
$element['#attributes'] = $attributes->toArray();
// Make sure element gets rendered again.
unset($element['#printed']);
return $element;
}
However, calling $element['#attributes']
is not currently working with an SDC component, causing unexpected behaviours.
Proposed resolution
Simply move the value of #attributes
property to $element["#props"]["attributes"]
before the rendering.
You can take inspiration from ComponentElementAlter::processAttributesRenderProperty
public function processAttributesRenderProperty(array $element): array {
if (!isset($element["#attributes"])) {
return $element;
}
if (is_a($element["#attributes"], '\Drupal\Core\Template\Attribute')) {
$element["#attributes"] = $element["#attributes"]->toArray();
}
// Like \Drupal\Core\Template\Attribute::merge(), we use
// NestedArray::mergeDeep().
// This function is similar to PHP's array_merge_recursive() function, but
// it handles non-array values differently. When merging values that are
// not both arrays, the latter value replaces the former rather than
// merging with it.
$element["#props"]["attributes"] = NestedArray::mergeDeep(
$element["#attributes"],
$element["#props"]["attributes"] ?? []
);
return $element;
}
And add a related test.
API changes
We don't break anything, we just improve compatibility with existing Drupal mechanisms.