Skip to content

Commit e214234

Browse files
authored
Add support for object-valued class attributes in Vue templates (#41)
Vue supports passing arrays and objects as the value of `:class=` attributes in templates, and adding `class=` attributes to the output HTML based on the data in the array / object. Add handling for array- / object-valued `:class=` attributes. Bug: T398012
1 parent 82eed3b commit e214234

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed

src/Component.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use DOMNode;
99
use DOMNodeList;
1010
use DOMText;
11+
use RuntimeException;
1112

1213
class Component {
1314

@@ -136,6 +137,26 @@ private function handleComponent( DOMElement $node, array $data ): bool {
136137
return true;
137138
}
138139

140+
private function handleArrayAttributeBinding( DOMElement $node, string $name, array $value ) {
141+
if ( $name !== 'class' ) {
142+
throw new RuntimeException( 'Array-valued data invalid for "' . $name . '" attribute' );
143+
}
144+
$existingParts = [];
145+
if ( $node->getAttribute( $name ) ) {
146+
$existingParts = explode( " ", $node->getAttribute( $name ) );
147+
}
148+
if ( array_is_list( $value ) ) {
149+
$existingParts = array_merge( $existingParts, $value );
150+
} else {
151+
foreach ( $value as $key => $addKey ) {
152+
if ( $addKey ) {
153+
array_unshift( $existingParts, $key );
154+
}
155+
}
156+
}
157+
$node->setAttribute( $name, implode( " ", $existingParts ) );
158+
}
159+
139160
private function handleAttributeBinding( DOMElement $node, array $data ) {
140161
/** @var DOMAttr $attribute */
141162
foreach ( iterator_to_array( $node->attributes ) as $attribute ) {
@@ -150,6 +171,8 @@ private function handleAttributeBinding( DOMElement $node, array $data ) {
150171
if ( $value ) {
151172
$node->setAttribute( $name, $name );
152173
}
174+
} elseif ( is_array( $value ) ) {
175+
$this->handleArrayAttributeBinding( $node, $name, $value );
153176
} else {
154177
$node->setAttribute( $name, $value );
155178
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<template id="template">
2+
<div class="wikibase-mex-statement-list">
3+
<div id="P123" class="wikibase-mex-statement">
4+
<div class="wikibase-mex-statement-heading">
5+
<div class="wikibase-mex-statement-heading-row">
6+
<div class="wikibase-mex-property-name">
7+
<p><a href="#" class="mex-link">country of citizenship</a></p>
8+
</div>
9+
<div class="wikibase-mex-edit-link">
10+
<span class="wikibase-mex-icon-edit-small"></span>
11+
<a href="#" class="mex-link-heavy">edit</a>
12+
</div>
13+
</div>
14+
</div>
15+
<div class="wikibase-mex-snak-value">
16+
<p><a href="#" class="mex-link">Barbados</a></p>
17+
</div>
18+
<div class="wikibase-mex-references">
19+
<p class="wikibase-mex-clickable" v-on:click="showReferences.P123 = !showReferences.P123">
20+
<span :class="{ 'wikibase-mex-icon-expand-x-small': !showReferences.P123, 'wikibase-mex-icon-collapse-x-small': showReferences.P123 }"></span>
21+
<a href="javascript: void(0)" class="mex-link">1 reference</a>
22+
</p>
23+
<div class="wikibase-mex-reference-list" :class="{ 'wikibase-mex-references-visible': showReferences.P123 }">
24+
<p>References</p>
25+
</div>
26+
</div>
27+
</div>
28+
<div id="P321" class="wikibase-mex-statement">
29+
<div class="wikibase-mex-statement-heading">
30+
<div class="wikibase-mex-statement-heading-row">
31+
<div class="wikibase-mex-property-name">
32+
<p><a href="#" class="mex-link">signature</a></p>
33+
</div>
34+
<div class="wikibase-mex-edit-link">
35+
<span class="wikibase-mex-icon-edit-small"></span>
36+
<a href="#" class="mex-link-heavy">edit</a>
37+
</div>
38+
</div>
39+
</div>
40+
<div class="wikibase-mex-snak-value">
41+
<div class="wikibase-mex-media-value">
42+
<div class="wikibase-mex-media-value-row">
43+
<div class="wikibase-mex-media-preview">
44+
<div class="wikibase-rankselector ui-state-default">
45+
<span class="ui-icon ui-icon-rankselector wikibase-rankselector-normal" title="Normal rank"></span>
46+
</div>
47+
<img :src="mediaInfo.src" :alt="mediaInfo.altText">
48+
</div>
49+
<div class="wikibase-mex-media-info">
50+
<p><a href="#" class="mex-link-heavy">{{ mediaInfo.filename }}</a></p>
51+
<p>{{ mediaInfo.widthPx }} <span>×</span> {{ mediaInfo.heightPx }}; {{ mediaInfo.fileSizeKb }} KB</p>
52+
</div>
53+
</div>
54+
</div>
55+
</div>
56+
<div class="wikibase-mex-references">
57+
<p :class="{ 'wikibase-mex-clickable': hasReferences }" v-on:click="showReferences.P321 = !showReferences.P321">
58+
<span v-if="hasReferences" :class="{ 'wikibase-mex-icon-expand-x-small': !showReferences.P321, 'wikibase-mex-icon-collapse-x-small': showReferences.P321 }"></span>
59+
<a v-if="hasReferences" href="javascript: void(0)" class="mex-link">{{ referencesMessage }}</a>
60+
<span v-else>{{ referencesMessage }}</span>
61+
</p>
62+
<div v-if="hasReferences" class="wikibase-mex-reference-list" :class="{ 'wikibase-mex-references-visible': showReferences.P321 }">
63+
<div v-for="reference in mediaInfo.references" :key="reference">
64+
<p><a href="#" class="mex-link">Reference URL</a></p>
65+
<p class="wikibase-mex-reference-link">
66+
<a class="mex-link" :href="reference">{{ reference }}</a>
67+
</p>
68+
</div>
69+
</div>
70+
</div>
71+
</div>
72+
</div>
73+
</template>
74+
75+
<script id="data" type="application/json">
76+
{
77+
"mediaInfo": {
78+
"src": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Rihanna-signature.svg/250px-Rihanna-signature.svg.png",
79+
"altText": "Some alt text",
80+
"filename": "Rihanna-signature.svg",
81+
"widthPx": 348,
82+
"heightPx": 178,
83+
"fileSizeKb": 9,
84+
"references": [
85+
"https://www.elle.com/culture/celebrities/a42622947/asap-rocky-rihanna-relationship-timeline/"
86+
]
87+
},
88+
"showReferences": {
89+
"P123": true,
90+
"P321": false
91+
},
92+
"referencesMessage": "1 reference",
93+
"hasReferences": true
94+
}
95+
</script>
96+
<div id="result">
97+
<!-- generated by `npm run-script populate-fixtures` -->
98+
<div class="wikibase-mex-statement-list"><div id="P123" class="wikibase-mex-statement"><div class="wikibase-mex-statement-heading"><div class="wikibase-mex-statement-heading-row"><div class="wikibase-mex-property-name"><p><a href="#" class="mex-link">country of citizenship</a></p></div><div class="wikibase-mex-edit-link"><span class="wikibase-mex-icon-edit-small"></span><a href="#" class="mex-link-heavy">edit</a></div></div></div><div class="wikibase-mex-snak-value"><p><a href="#" class="mex-link">Barbados</a></p></div><div class="wikibase-mex-references"><p class="wikibase-mex-clickable"><span class="wikibase-mex-icon-collapse-x-small"></span><a href="javascript: void(0)" class="mex-link">1 reference</a></p><div class="wikibase-mex-references-visible wikibase-mex-reference-list"><p>References</p></div></div></div><div id="P321" class="wikibase-mex-statement"><div class="wikibase-mex-statement-heading"><div class="wikibase-mex-statement-heading-row"><div class="wikibase-mex-property-name"><p><a href="#" class="mex-link">signature</a></p></div><div class="wikibase-mex-edit-link"><span class="wikibase-mex-icon-edit-small"></span><a href="#" class="mex-link-heavy">edit</a></div></div></div><div class="wikibase-mex-snak-value"><div class="wikibase-mex-media-value"><div class="wikibase-mex-media-value-row"><div class="wikibase-mex-media-preview"><div class="wikibase-rankselector ui-state-default"><span class="ui-icon ui-icon-rankselector wikibase-rankselector-normal" title="Normal rank"></span></div><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Rihanna-signature.svg/250px-Rihanna-signature.svg.png" alt="Some alt text"></div><div class="wikibase-mex-media-info"><p><a href="#" class="mex-link-heavy">Rihanna-signature.svg</a></p><p>348 <span>×</span> 178; 9 KB</p></div></div></div></div><div class="wikibase-mex-references"><p class="wikibase-mex-clickable"><span class="wikibase-mex-icon-expand-x-small"></span><a href="javascript: void(0)" class="mex-link">1 reference</a></p><div class="wikibase-mex-reference-list"><!--[--><div><p><a href="#" class="mex-link">Reference URL</a></p><p class="wikibase-mex-reference-link"><a class="mex-link" href="https://www.elle.com/culture/celebrities/a42622947/asap-rocky-rihanna-relationship-timeline/">https://www.elle.com/culture/celebrities/a42622947/asap-rocky-rihanna-relationship-timeline/</a></p></div><!--]--></div></div></div></div>
99+
</div>

tests/php/TemplatingTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,41 @@ public function testMustacheAfterVIf(): void {
311311
$this->assertSame( '<p>a: A c: C</p>', $result );
312312
}
313313

314+
public function testTemplateWithArrayValuedClassAttribute() {
315+
$result = $this->createAndRender(
316+
'<p><a :class="list">Link</a></p>',
317+
[ 'list' => [ 'one_class', 'another_class' ] ]
318+
);
319+
320+
$this->assertSame( '<p><a class="one_class another_class">Link</a></p>', $result );
321+
}
322+
323+
public function testTemplateWithObjectValuedClassAttribute() {
324+
$result = $this->createAndRender(
325+
'<p><a :class="list">Link</a></p>',
326+
[ 'list' => [ 'one_class' => true, 'another_class' => false ] ]
327+
);
328+
329+
$this->assertSame( '<p><a class="one_class">Link</a></p>', $result );
330+
}
331+
332+
public function testTemplateWithObjectValuedClassAttribute_UnionWithExistingAttributes() {
333+
$result = $this->createAndRender(
334+
'<p><a class="another_class" :class="list">Link</a></p>',
335+
[ 'list' => [ 'one_class' => true, 'another_class' => false ] ]
336+
);
337+
338+
$this->assertSame( '<p><a class="one_class another_class">Link</a></p>', $result );
339+
}
340+
341+
public function testTemplateWithObjectValuedNonClassAttribute_ThrowsError() {
342+
$this->expectException( Exception::class );
343+
$this->createAndRender(
344+
'<p><a :my-attr="list">Link</a></p>',
345+
[ 'list' => [ 'one_class' => true, 'another_class' => false ] ]
346+
);
347+
}
348+
314349
/**
315350
* @param string $template HTML
316351
* @param array $data

0 commit comments

Comments
 (0)