-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an attr
function to make outputting HTML attributes easier
#3930
base: 3.x
Are you sure you want to change the base?
Conversation
b680b5f
to
9c29c4d
Compare
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 it a lot. Let's continue the work. Tell me of you need help @mpdude
extra/html-extra/HtmlExtension.php
Outdated
|
||
$result = ''; | ||
foreach ($attr as $name => $value) { | ||
$result .= twig_escape_filter($env, $name, 'html_attr').'="'.htmlspecialchars($value).'" '; |
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.
Let's consider false
as a way to disable the attribute? And true
would not generate the value part?
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 guess that makes sense. I'd have false
omit the attribute output altogether. For true
, I'd use something like name="name"
for backwards compat with (X)HTML.
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.
Almost everywhere else, indeed, false
should remove/hide the attribute.
""
should not happen, i guess, but should be dealt with attention, because the following have all the same meaning:
disabled=""
disabled
disabled="false"
disabled="disabled"
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.
For the aria cases, if you want, there is some classic cases + docs here
with some test cases if you need too https://github.com/symfony/ux/blob/647532ab688f79acfbcb1c895e88a8b2f1a502f6/src/Icons/tests/Unit/IconTest.php#L226
(other similar in the TwigComponent / ComponentAttributes)
f65ee8f
to
c9acca6
Compare
f7c3b7a
to
c7bacfd
Compare
c7bacfd
to
03a5de1
Compare
@fabpot Regarding tests: I suppose the fixture-based tests ( What's the best way to cover lots of scenarios in the fixture-style tests? It's easy to get lost when there are lots of cases but just one |
You can write both, but
That's indeed a current limitation. |
@@ -124,4 +130,85 @@ public static function htmlCva(array|string $base = [], array $variants = [], ar | |||
{ | |||
return new Cva($base, $variants, $compoundVariants, $defaultVariant); | |||
} | |||
|
|||
public static function htmlAttrMerge(...$arrays): array |
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 can maybe have a @param
to better describe $arrays
?
return $result; | ||
} | ||
|
||
public static function htmlAttr(Environment $env, ...$args): string |
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.
Maybe a @param
for $args
?
{ | ||
$result = []; | ||
|
||
foreach ($arrays as $argNumber => $array) { |
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.
Not guaranteed that we have a number here.
extra/html-extra/HtmlExtension.php
Outdated
|
||
$value = CoreExtension::toArray($value); | ||
|
||
$result[$deepMergeKey] = array_merge($result[$deepMergeKey] ?? [], $value); |
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.
Can we use [... ]
when we can ? As it's slightly more performant than array_merge
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 (is_numeric($name)) { | ||
$style .= $value.'; '; | ||
} else { | ||
$style .= $name.': '.$value.'; '; | ||
} |
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'm not sure why this specific case for style.. i may have missed the reason earlier in the discussion ?
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 case style
is something like ['color: red', 'font-weight: bold']
.
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 have two other questions then (sorry)
What if "aria" is something like ['aria-disabled', 'aria-current']
?
Does that mean i can do something like this ?
{{ html_attr(
{ style: ['color: red;'] },
{ style: {'color': 'blue'} },
{ style: {'color': 'green'} },
{ style: ['', 'color: black;'] },
) }}
I'm shared between "i understand each of these usages" and "this could be very un-predictable when/if args are assembled from different layers / templates :|
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.
Yes, definetly many edge cases I haven't thought through. Make sure you don't miss the inital idea in #3907, fwiw.
I think we should aim for behaviour similar to array_merge
and not try to do any kind of parsing/interpretation. For example:
html_attr_merge( { style: {'color': 'blue'} }, { style: {'color': 'green'} } } ) }}
would be{ style: {'color': 'green'} }
html_attr_merge( { style: ['color: blue'] }, { style: { ['color: green']} } } ) }}
would be{ style: ['color: blue', 'color: green'] }
– if you want "real" overrides, use semantic keyshtml_attr_merge( { aria: { 'aria-label': 'that' }, {'label': 'this'} } )
would be{ 'aria-label': 'this' }
– I'd rewrite keys from thearia
anddata
sub-array to the "flat" values first and then merge those.
I hope I can come up with reasonable test cases for all those situations so we can discuss them before merge.
cf673c1
to
9722108
Compare
Added a first load of tests for the |
Co-authored-by: Fabien Potencier <[email protected]>
Could this be implemented as a standalone class which handles the core functionality similar to the way This would allow twig ux components to do custom attribute merging in a For reference Yii2 has a similar function: https://github.com/yiisoft/yii2/blob/master/framework/helpers/BaseHtml.php#L1966-L2046 The renderTagAttributes method has the following rules:
CraftCMS uses twig and provides an |
Closes #3907.
First, it adds a
attr_merge
filter. This filter is intended to be used with arrays that represent HTML attribute name-value pairs. Basically, it works like|merge
, but for the special key namesclass
,style
anddata
it performs merging on thevalue
level.This is intended for the use case where you'd like to add to the attributes for an HTML element based on conditions, e. g. multiple subsequent
{% if ... %}
blocks.Example:
{ class: 'foo' }|attr_merge({ class: 'bar' })
will be{ class: ['foo', 'bar'] }
{ class: 'foo' }|attr_merge({ class: ['bar', 'baz'] })
will be{ class: ['foo', 'bar', 'baz'] }
{ class: { special: 'foo' } }|attr_merge({ class: ['bar', 'baz'] })|attr_merge({ class: {special: 'qux' } })
will be{ class: { special: 'qux', 0: 'bar', 1: 'baz' } }
or in Twig code:
Second, it adds a
attr
function. This function takes one or multipleattr
arrays like above, and print them as a series of HTML attribute markup. All values fromclass
will be concatenated with spaces. Key/value pairs fromstyle
will be treated as CSS property/value pairs. Fordata
, keys will be used to constructdata-{keyname}
attributes.Example:
will generate HTML markup:
TODO: