Loading core/lib/Drupal/Core/Render/Element/ComponentElement.php +7 −4 Original line number Diff line number Diff line Loading @@ -162,19 +162,22 @@ private function generateComponentTemplate( * The render element. */ private function mergeElementAttributesToPropAttributes(array &$element): void { // Prepare #props attributes to be both mergeable and renderable. $prop_attributes = $element['#props']['attributes'] ?? []; $prop_attributes = is_array($prop_attributes) ? new Attribute($prop_attributes) : $prop_attributes; if (!isset($element['#attributes'])) { $element['#props']['attributes'] = $prop_attributes; return; } // If attributes value is an array, convert it to an Attribute object as // \Drupal\Core\Template\Atribute::merge() expects an Attribute object. // \Drupal\Core\Template\Attribute::merge() expects an Attribute object. $element_attributes = is_array($element['#attributes']) ? new Attribute($element['#attributes']) : $element['#attributes']; // Merge ['#attributes'] with the ['#props']['attributes']. So that #props // attributes take precedence. $element['#props']['attributes'] = empty($element['#props']['attributes']) ? $element_attributes : $element_attributes->merge($element['#props']['attributes']); $element['#props']['attributes'] = $element_attributes->merge($prop_attributes); } /** Loading core/tests/Drupal/KernelTests/Components/ComponentRenderTest.php +55 −1 Original line number Diff line number Diff line Loading @@ -242,6 +242,7 @@ public function testRenderAttributeMerging(): void { */ public function testCheckElementAttributesAndPropAttributesMerging(): void { // Test no attributes. // Prop: no. Element: no. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -254,6 +255,7 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $this->assertEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][bar="fpo"]'), $crawler->outerHtml()); // Test for just prop attributes. // Prop: object. Element: no. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -268,6 +270,7 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"]'), $crawler->outerHtml()); // Test for prop attributes and element attributes as Attribute object. // Prop: object. Element: object. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -285,6 +288,7 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"][bar="foo"]'), $crawler->outerHtml()); // Test for no prop attributes and element attributes as Attribute object. // Prop: no. Element: object. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -299,6 +303,7 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][bar="foo"]'), $crawler->outerHtml()); // Test for prop attributes and element attributes as Attribute array. // Prop: object. Element: array. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -315,7 +320,8 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"][bar="foo"]'), $crawler->outerHtml()); // Test for no prop attributes and element attributes as Attribute array. // Test for no prop attributes and element attributes as array. // Prop: no. Element: array. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -329,6 +335,54 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][bar="foo"]'), $crawler->outerHtml()); // Prop: array. Element: no. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', '#props' => [ 'header' => 'Drupal.org', 'attributes' => [ 'foo' => 'bar', ], ], ]; $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"]'), $crawler->outerHtml()); // Prop: array. Element: object. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', '#props' => [ 'header' => 'Drupal.org', 'attributes' => [ 'foo' => 'bar', ], ], '#attributes' => new Attribute([ 'bar' => 'foo', ]), ]; $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"][bar="foo"]'), $crawler->outerHtml()); // Prop: array. Element: array. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', '#props' => [ 'header' => 'Drupal.org', 'attributes' => [ 'foo' => 'bar', ], ], '#attributes' => [ 'bar' => 'foo', ], ]; $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"][bar="foo"]'), $crawler->outerHtml()); // Test overlapping attributes. $build = [ '#type' => 'component', Loading Loading
core/lib/Drupal/Core/Render/Element/ComponentElement.php +7 −4 Original line number Diff line number Diff line Loading @@ -162,19 +162,22 @@ private function generateComponentTemplate( * The render element. */ private function mergeElementAttributesToPropAttributes(array &$element): void { // Prepare #props attributes to be both mergeable and renderable. $prop_attributes = $element['#props']['attributes'] ?? []; $prop_attributes = is_array($prop_attributes) ? new Attribute($prop_attributes) : $prop_attributes; if (!isset($element['#attributes'])) { $element['#props']['attributes'] = $prop_attributes; return; } // If attributes value is an array, convert it to an Attribute object as // \Drupal\Core\Template\Atribute::merge() expects an Attribute object. // \Drupal\Core\Template\Attribute::merge() expects an Attribute object. $element_attributes = is_array($element['#attributes']) ? new Attribute($element['#attributes']) : $element['#attributes']; // Merge ['#attributes'] with the ['#props']['attributes']. So that #props // attributes take precedence. $element['#props']['attributes'] = empty($element['#props']['attributes']) ? $element_attributes : $element_attributes->merge($element['#props']['attributes']); $element['#props']['attributes'] = $element_attributes->merge($prop_attributes); } /** Loading
core/tests/Drupal/KernelTests/Components/ComponentRenderTest.php +55 −1 Original line number Diff line number Diff line Loading @@ -242,6 +242,7 @@ public function testRenderAttributeMerging(): void { */ public function testCheckElementAttributesAndPropAttributesMerging(): void { // Test no attributes. // Prop: no. Element: no. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -254,6 +255,7 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $this->assertEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][bar="fpo"]'), $crawler->outerHtml()); // Test for just prop attributes. // Prop: object. Element: no. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -268,6 +270,7 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"]'), $crawler->outerHtml()); // Test for prop attributes and element attributes as Attribute object. // Prop: object. Element: object. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -285,6 +288,7 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"][bar="foo"]'), $crawler->outerHtml()); // Test for no prop attributes and element attributes as Attribute object. // Prop: no. Element: object. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -299,6 +303,7 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][bar="foo"]'), $crawler->outerHtml()); // Test for prop attributes and element attributes as Attribute array. // Prop: object. Element: array. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -315,7 +320,8 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"][bar="foo"]'), $crawler->outerHtml()); // Test for no prop attributes and element attributes as Attribute array. // Test for no prop attributes and element attributes as array. // Prop: no. Element: array. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', Loading @@ -329,6 +335,54 @@ public function testCheckElementAttributesAndPropAttributesMerging(): void { $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][bar="foo"]'), $crawler->outerHtml()); // Prop: array. Element: no. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', '#props' => [ 'header' => 'Drupal.org', 'attributes' => [ 'foo' => 'bar', ], ], ]; $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"]'), $crawler->outerHtml()); // Prop: array. Element: object. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', '#props' => [ 'header' => 'Drupal.org', 'attributes' => [ 'foo' => 'bar', ], ], '#attributes' => new Attribute([ 'bar' => 'foo', ]), ]; $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"][bar="foo"]'), $crawler->outerHtml()); // Prop: array. Element: array. $build = [ '#type' => 'component', '#component' => 'sdc_theme_test:my-card', '#props' => [ 'header' => 'Drupal.org', 'attributes' => [ 'foo' => 'bar', ], ], '#attributes' => [ 'bar' => 'foo', ], ]; $crawler = $this->renderComponentRenderArray($build); $this->assertNotEmpty($crawler->filter('#sdc-wrapper [data-component-id="sdc_theme_test:my-card"][foo="bar"][bar="foo"]'), $crawler->outerHtml()); // Test overlapping attributes. $build = [ '#type' => 'component', Loading