Skip to content

Commit af957de

Browse files
committed
refactor attributes api
1 parent e856cb5 commit af957de

File tree

6 files changed

+32
-69
lines changed

6 files changed

+32
-69
lines changed

src/TwigComponent/src/ComponentAttributes.php

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,24 @@ public function all(): array
4646
return $this->attributes;
4747
}
4848

49+
/**
50+
* Set default attributes. These are used if this object doesn't
51+
* define them. If it does, they are replaced except for "class".
52+
* For "class", these are prepended.
53+
*/
4954
public function merge(array $with): self
5055
{
5156
foreach ($this->attributes as $key => $value) {
52-
$with[$key] = isset($with[$key]) ? "{$with[$key]} {$value}" : $value;
57+
// "class" is special so we prepend defaults
58+
$with[$key] = isset($with[$key]) && 'class' === $key ? "{$with[$key]} {$value}" : $value;
5359
}
5460

5561
return new self($with);
5662
}
5763

64+
/**
65+
* Extract only these attributes.
66+
*/
5867
public function only(string ...$keys): self
5968
{
6069
$attributes = [];
@@ -68,6 +77,9 @@ public function only(string ...$keys): self
6877
return new self($attributes);
6978
}
7079

80+
/**
81+
* Extract all but these attributes.
82+
*/
7183
public function without(string ...$keys): self
7284
{
7385
$clone = clone $this;
@@ -78,23 +90,4 @@ public function without(string ...$keys): self
7890

7991
return $clone;
8092
}
81-
82-
/**
83-
* @param array<string, string> $attributes
84-
*/
85-
public function defaults(array $attributes): self
86-
{
87-
$clone = $this;
88-
89-
foreach ($attributes as $attribute => $value) {
90-
$clone->attributes[$attribute] = $clone->attributes[$attribute] ?? $value;
91-
}
92-
93-
return $clone;
94-
}
95-
96-
public function default(string $attribute, string $value): self
97-
{
98-
return $this->defaults([$attribute => $value]);
99-
}
10093
}

src/TwigComponent/src/Resources/doc/index.rst

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ To use, add the HasAttributesTrait to your component:
436436
use HasAttributesTrait;
437437
}
438438

439+
Then render the attributes on the root element:
440+
439441
.. code-block:: twig
440442
441443
{# templates/components/my_component.html.twig #}
@@ -455,50 +457,30 @@ When rendering the component, you can pass an array of html attributes to add:
455457
My Component!
456458
</div>
457459
458-
Defaults
459-
~~~~~~~~
460+
Defaults & Merging
461+
~~~~~~~~~~~~~~~~~~
460462

461-
Set default attributes that can be fully overridden by passed attributes:
463+
In your component template, you can set defaults that are merged with
464+
passed attributes. The passed attributes override the default with
465+
the exception of *class*. For class, the defaults are prepended:
462466

463467
.. code-block:: twig
464468
465469
{# templates/components/my_component.html.twig #}
466470
467-
<div{{ attributes.defaults({ class: 'bar' }) }}>
471+
<button{{ attributes.defaults({ class: 'bar', type: 'button' }) }}>
468472
My Component!
469-
</div>
473+
</button>
470474
471475
{# render component #}
472476
{{ component('my_component', { style: 'color:red' }) }}
473-
{{ component('my_component', { class: 'foo', style: 'color:red' }) }}
477+
{{ component('my_component', { class: 'foo', type: 'submit' }) }}
474478
475479
{# renders as: #}
476480
<div class="bar" style="color:red">
477481
My Component!
478482
</div>
479-
<div class="foo" style="color:red">
480-
My Component!
481-
</div>
482-
483-
Merging Defaults
484-
~~~~~~~~~~~~~~~~
485-
486-
Set defaults but allow them to be appended to by passing these values to
487-
the ``component()`` function:
488-
489-
.. code-block:: twig
490-
491-
{# templates/components/my_component.html.twig #}
492-
493-
<div{{ attributes.merge({ class: 'bar' }) }}>
494-
My Component!
495-
</div>
496-
497-
{# render component #}
498-
{{ component('my_component', { class: 'foo', style: 'color:red' }) }}
499-
500-
{# renders as: #}
501-
<div class="bar foo" style="color:red">
483+
<div class="bar foo" type="submit">
502484
My Component!
503485
</div>
504486
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div{{ attributes.merge({ class: 'foo' }) }}>Component Content ({{ prop }})</div>
1+
<button{{ attributes.merge({ class: 'foo', type: 'button' }) }}>Component Content ({{ prop }})</button>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{{ component('component_a', { propA: 'prop a value', propB: 'prop b value' }) }}
22
{{ component('with_attributes', { prop: 'prop value 1', class: 'bar', style: 'color:red;' }) }}
3-
{{ component('with_attributes', { prop: 'prop value 2', attributes: { class: 'baz' }, style: 'color:red;' }) }}
3+
{{ component('with_attributes', { prop: 'prop value 2', attributes: { class: 'baz' }, type: 'submit', style: 'color:red;' }) }}

src/TwigComponent/tests/Integration/ComponentExtensionTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public function testCanRenderComponentWithAttributes(): void
6262
$output = self::getContainer()->get(Environment::class)->render('template_a.html.twig');
6363

6464
$this->assertStringContainsString('Component Content (prop value 1)', $output);
65-
$this->assertStringContainsString('<div class="foo bar" style="color:red;">', $output);
65+
$this->assertStringContainsString('<button class="foo bar" type="button" style="color:red;">', $output);
6666
$this->assertStringContainsString('Component Content (prop value 2)', $output);
67-
$this->assertStringContainsString('<div class="foo baz" style="color:red;">', $output);
67+
$this->assertStringContainsString('<button class="foo baz" type="submit" style="color:red;">', $output);
6868
}
6969
}

src/TwigComponent/tests/Unit/ComponentAttributesTest.php

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,20 @@ public function testCanConvertToString(): void
2626
$this->assertSame(' class="foo" style="color:black;"', (string) $attributes);
2727
}
2828

29-
public function testCanSetDefaults(): void
30-
{
31-
$attributes = new ComponentAttributes(['class' => 'foo', 'style' => 'color:black;']);
32-
33-
$this->assertSame(
34-
['class' => 'foo', 'style' => 'color:black;'],
35-
$attributes->default('class', 'bar')->all()
36-
);
37-
$this->assertSame(
38-
['class' => 'foo', 'style' => 'color:black;', 'id' => '1'],
39-
$attributes->defaults(['id' => '1'])->all()
40-
);
41-
}
42-
4329
public function testCanMerge(): void
4430
{
4531
$attributes = new ComponentAttributes(['class' => 'foo', 'style' => 'color:black;']);
4632

4733
$this->assertSame(
48-
['class' => 'bar foo', 'style' => 'font-size: 10; color:black;'],
34+
['class' => 'bar foo', 'style' => 'color:black;'],
4935
$attributes->merge(['class' => 'bar', 'style' => 'font-size: 10;'])->all()
5036
);
5137
$this->assertSame(
52-
' class="bar foo" style="font-size: 10; color:black;"',
38+
' class="bar foo" style="color:black;"',
5339
(string) $attributes->merge(['class' => 'bar', 'style' => 'font-size: 10;'])
5440
);
41+
42+
$this->assertSame(['class' => 'foo'], (new ComponentAttributes([]))->merge(['class' => 'foo'])->all());
5543
}
5644

5745
public function testCanGetOnly(): void

0 commit comments

Comments
 (0)