Commit 65cb382c authored by alexpott's avatar alexpott

Issue #2285451 by joelpittet, davidhernandez, cilefen, mdrummond, mortendk:...

Issue #2285451 by joelpittet, davidhernandez, cilefen, mdrummond, mortendk: Create addClass() and removeClass() methods on Attribute object for merging css class names.
parent 008fb117
......@@ -80,8 +80,18 @@ public function offsetSet($name, $value) {
* An AttributeValueBase representation of the attribute's value.
*/
protected function createAttributeValue($name, $value) {
if (is_array($value)) {
$value = new AttributeArray($name, $value);
// If the value is already an AttributeValueBase object, return it
// straight away.
if ($value instanceOf AttributeValueBase) {
return $value;
}
// An array value or 'class' attribute name are forced to always be an
// AttributeArray value for consistency.
if (is_array($value) || $name == 'class') {
// Cast the value to an array if the value was passed in as a string.
// @todo Decide to fix all the broken instances of class as a string
// in core or cast them.
$value = new AttributeArray($name, (array) $value);
}
elseif (is_bool($value)) {
$value = new AttributeBoolean($name, $value);
......@@ -106,6 +116,68 @@ public function offsetExists($name) {
return isset($this->storage[$name]);
}
/**
* Adds argument values by merging them on to array of existing CSS classes.
*
* @param string|array ...
* CSS classes to add to the class attribute array.
*
* @return $this
*/
public function addClass() {
$args = func_get_args();
$classes = array();
foreach ($args as $arg) {
// Merge the values passed in from the classes array.
// The argument is cast to an array to support comma separated single
// values or one or more array arguments.
$classes = array_merge($classes, (array) $arg);
}
// Merge if there are values, just add them otherwise.
if (isset($this->storage['class']) && $this->storage['class'] instanceOf AttributeArray) {
// Merge the values passed in from the class value array.
$classes = array_merge($this->storage['class']->value(), $classes);
// Filter out any duplicate values.
$classes = array_unique($classes);
$this->storage['class']->exchangeArray($classes);
}
else {
// Filter out any duplicate values.
$classes = array_unique($classes);
$this->offsetSet('class', $classes);
}
return $this;
}
/**
* Removes argument values from array of existing CSS classes.
*
* @param string|array ...
* CSS classes to remove from the class attribute array.
*
* @return $this
*/
public function removeClass() {
// With no class attribute, there is no need to remove.
if (isset($this->storage['class']) && $this->storage['class'] instanceOf AttributeArray) {
$args = func_get_args();
$classes = array();
foreach ($args as $arg) {
// Merge the values passed in from the classes array.
// The argument is cast to an array to support comma separated single
// values or one or more array arguments.
$classes = array_merge($classes, (array) $arg);
}
// Remove the values passed in from the value array.
$classes = array_diff($this->storage['class']->value(), $classes);
$this->storage['class']->exchangeArray($classes);
}
return $this;
}
/**
* Implements the magic __toString() method.
*/
......
......@@ -83,4 +83,21 @@ public function value() {
return $this->value;
}
/**
* Exchange the array for another one.
*
* @see ArrayObject::exchangeArray
*
* @param array $input
* The array input to replace the internal value.
*
* @return array
* The old array value.
*/
public function exchangeArray($input) {
$old = $this->value;
$this->value = $input;
return $old;
}
}
......@@ -57,6 +57,130 @@ public function testRemove() {
$this->assertFalse(isset($attribute['class']));
}
/**
* Tests adding class attributes with the AttributeArray helper method.
* @covers ::addClass()
*/
public function testAddClasses() {
// Add empty Attribute object with no classes.
$attribute = new Attribute();
// Add one class on empty attribute.
$attribute->addClass('banana');
$this->assertArrayEquals(array('banana'), $attribute['class']->value());
// Add one class.
$attribute->addClass('aa');
$this->assertArrayEquals(array('banana', 'aa'), $attribute['class']->value());
// Add multiple classes.
$attribute->addClass('xx', 'yy');
$this->assertArrayEquals(array('banana', 'aa', 'xx', 'yy'), $attribute['class']->value());
// Add an array of classes.
$attribute->addClass(array('red', 'green', 'blue'));
$this->assertArrayEquals(array('banana', 'aa', 'xx', 'yy', 'red', 'green', 'blue'), $attribute['class']->value());
// Add an array of duplicate classes.
$attribute->addClass(array('red', 'green', 'blue'), array('aa', 'aa', 'banana'), 'yy');
$this->assertArrayEquals(array('banana', 'aa', 'xx', 'yy', 'red', 'green', 'blue'), $attribute['class']->value());
}
/**
* Tests removing class attributes with the AttributeArray helper method.
* @covers ::removeClass()
*/
public function testRemoveClasses() {
$classes = array('example-class', 'aa', 'xx', 'yy', 'red', 'green', 'blue');
$attribute = new Attribute(array('class' => $classes));
// Remove one class.
$attribute->removeClass('example-class');
$this->assertNotContains('example-class', $attribute['class']->value());
// Remove multiple classes.
$attribute->removeClass('xx', 'yy');
$this->assertNotContains(array('xx', 'yy'), $attribute['class']->value());
// Remove an array of classes.
$attribute->removeClass(array('red', 'green', 'blue'));
$this->assertNotContains(array('red', 'green', 'blue'), $attribute['class']->value());
}
/**
* Tests removing class attributes with the Attribute helper methods.
* @covers ::removeClass()
* @covers ::addClass()
*/
public function testChainAddRemoveClasses() {
$attribute = new Attribute(
array('class' => array('example-class', 'red', 'green', 'blue'))
);
$attribute
->removeClass(array('red', 'green', 'pink'))
->addClass(array('apple', 'lime', 'grapefruit'))
->addClass(array('banana'));
$expected = array('example-class', 'blue', 'apple', 'lime', 'grapefruit', 'banana');
$this->assertArrayEquals($expected, $attribute['class']->value(), 'Attributes chained');
}
/**
* Tests the twig calls to the Attribute.
* @dataProvider providerTestAttributeClassHelpers
*
* @covers ::removeClass()
* @covers ::addClass()
*/
public function testTwigAddRemoveClasses($template, $expected, $seed_attributes = array()) {
$loader = new \Twig_Loader_String();
$twig = new \Twig_Environment($loader);
$data = array('attributes' => new Attribute($seed_attributes));
$result = $twig->render($template, $data);
$this->assertEquals($expected, $result);
}
/**
* Provides tests data for testEscaping
*
* @return array
* An array of test data each containing of a twig template string,
* a resulting string of classes and an optional array of attributes.
*/
public function providerTestAttributeClassHelpers() {
return array(
array("{{ attributes.class }}", ''),
array("{{ attributes.addClass('everest').class }}", 'everest'),
array("{{ attributes.addClass(['k2', 'kangchenjunga']).class }}", 'k2 kangchenjunga'),
array("{{ attributes.addClass('lhotse', 'makalu', 'cho-oyu').class }}", 'lhotse makalu cho-oyu'),
array(
"{{ attributes.addClass('nanga-parbat').class }}",
'dhaulagiri manaslu nanga-parbat',
array('class' => array('dhaulagiri', 'manaslu')),
),
array(
"{{ attributes.removeClass('annapurna').class }}",
'gasherbrum-i',
array('class' => array('annapurna', 'gasherbrum-i')),
),
array(
"{{ attributes.removeClass(['broad peak']).class }}",
'gasherbrum-ii',
array('class' => array('broad peak', 'gasherbrum-ii')),
),
array(
"{{ attributes.removeClass('gyachung-kang', 'shishapangma').class }}",
'',
array('class' => array('shishapangma', 'gyachung-kang')),
),
array(
"{{ attributes.removeClass('nuptse').addClass('annapurna-ii').class }}",
'himalchuli annapurna-ii',
array('class' => array('himalchuli', 'nuptse')),
),
);
}
/**
* Tests iterating on the values of the attribute.
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment