Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 31 additions & 33 deletions src/View/StringTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,45 +345,43 @@ protected function formatAttribute(string $key, mixed $value, bool $escape = tru
}

/**
* Adds a class and returns a unique list either in array or space separated
* Adds CSS classes to an attributes array
*
* @param array<string, mixed>|string|null $input The array or string to add the class to
* @param array<string>|string|false|null $newClass the new class or classes to add
* @param string $useIndex if you are inputting an array with an element other than default of 'class'.
* @return array<string, string>|string|null
* @param array<string, mixed> $attributes The attributes array to add classes to
* @param array<string>|string $newClasses The new class or classes to add
* @param string $key The array key to use for the class list (default: 'class')
* @return array<string, mixed> The attributes array with added classes
*/
public function addClass(
mixed $input,
array|string|false|null $newClass,
string $useIndex = 'class',
): array|string|null {
// NOOP
if (!$newClass) {
return $input;
}

if (is_array($input)) {
$class = Hash::get($input, $useIndex, []);
} else {
$class = $input;
$input = [];
}
public function addClassToArray(
array $attributes,
array|string $newClasses,
string $key = 'class',
): array {
$existingClasses = Hash::get($attributes, $key, []);
$mergedClasses = $this->mergeClasses($existingClasses, $newClasses);

return Hash::insert($attributes, $key, $mergedClasses);
}

// Convert and sanitize the inputs
if (!is_array($class)) {
if (is_string($class) && !empty($class)) {
$class = explode(' ', $class);
} else {
$class = [];
}
/**
* Merges CSS classes from various formats into a unique array
*
* @param array<string>|string $existing Existing classes (array or space-separated string)
* @param array<string>|string $new New classes to add (array or space-separated string)
* @return array<string> Unique array of class names
*/
public function mergeClasses(array|string $existing, array|string $new): array
{
// Convert existing classes to array
if (!is_array($existing)) {
$existing = $existing !== '' ? explode(' ', (string)$existing) : [];
}

if (is_string($newClass)) {
$newClass = explode(' ', $newClass);
// Convert new classes to array
if (!is_array($new)) {
$new = $new !== '' ? explode(' ', (string)$new) : [];
}

$class = array_unique(array_merge($class, $newClass));

return Hash::insert($input, $useIndex, $class);
return array_values(array_unique(array_merge($existing, $new)));
}
}
2 changes: 1 addition & 1 deletion src/View/Widget/MultiCheckboxWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ protected function renderInput(array $checkbox, ContextInterface $context): stri

if ($checkbox['checked']) {
$selectedClass = $this->_templates->format('selectedClass', []);
$labelAttrs = (array)$this->_templates->addClass($labelAttrs, $selectedClass);
$labelAttrs = $this->_templates->addClassToArray((array)$labelAttrs, $selectedClass);
}

$label = $this->_label->render($labelAttrs, $context);
Expand Down
2 changes: 1 addition & 1 deletion src/View/Widget/RadioWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ protected function renderInput(

if (!is_bool($data['label']) && isset($radio['checked']) && $radio['checked']) {
$selectedClass = $this->_templates->format('selectedClass', []);
$data['label'] = $this->_templates->addClass($data['label'], $selectedClass);
$data['label'] = $this->_templates->addClassToArray((array)$data['label'], $selectedClass);
}

$radio['disabled'] = $this->isDisabled($radio, $data['disabled']);
Expand Down
204 changes: 104 additions & 100 deletions tests/TestCase/View/StringTemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
use Cake\TestSuite\TestCase;
use Cake\View\StringTemplate;
use InvalidArgumentException;
use stdClass;

class StringTemplateTest extends TestCase
{
Expand Down Expand Up @@ -304,122 +303,127 @@ public function testPushPopTemplates(): void
}

/**
* Test addClass method newClass parameter
*
* Tests null, string, array and false for `input`
* Test addClassToArray method with various inputs
*/
public function testAddClassMethodNewClass(): void
public function testAddClassToArray(): void
{
$result = $this->template->addClass([], 'new_class');
$this->assertEquals($result, ['class' => ['new_class']]);

$result = $this->template->addClass([], ['new_class']);
$this->assertEquals($result, ['class' => ['new_class']]);
// Test adding a single class to an empty array
$result = $this->template->addClassToArray([], 'new-class');
$this->assertEquals(['class' => ['new-class']], $result);

// Test adding multiple classes as array
$result = $this->template->addClassToArray([], ['class1', 'class2']);
$this->assertEquals(['class' => ['class1', 'class2']], $result);

// Test adding classes to existing array with classes
$result = $this->template->addClassToArray(
['class' => ['existing']],
'new-class',
);
$this->assertEquals(['class' => ['existing', 'new-class']], $result);

$result = $this->template->addClass([], false);
$this->assertEquals($result, []);
// Test with custom key
$result = $this->template->addClassToArray(
['id' => 'test'],
'custom-class',
'data-class',
);
$this->assertEquals([
'id' => 'test',
'data-class' => ['custom-class'],
], $result);

// Test merging with existing custom key
$result = $this->template->addClassToArray(
['data-class' => ['existing'], 'id' => 'test'],
'new-class',
'data-class',
);
$this->assertEquals([
'data-class' => ['existing', 'new-class'],
'id' => 'test',
], $result);

// Test uniqueness
$result = $this->template->addClassToArray(
['class' => ['duplicate']],
['duplicate', 'new'],
);
$this->assertEquals(['class' => ['duplicate', 'new']], $result);

$result = $this->template->addClass([], null);
$this->assertEquals($result, []);
// Test with string existing classes (should be converted to array)
$result = $this->template->addClassToArray(
['class' => 'existing-string'],
'new-class',
);
$this->assertEquals(['class' => ['existing-string', 'new-class']], $result);

$result = $this->template->addClass(null, null);
$this->assertNull($result);
// Test with space-separated string
$result = $this->template->addClassToArray(
['class' => 'class1 class2'],
'class3 class4',
);
$this->assertEquals(['class' => ['class1', 'class2', 'class3', 'class4']], $result);
}

/**
* Test addClass method input (currentClass) parameter
*
* Tests null, string, array, false and object
* Test mergeClasses method with various inputs
*/
public function testAddClassMethodCurrentClass(): void
public function testMergeClasses(): void
{
$result = $this->template->addClass(['class' => ['current']], 'new_class');
$this->assertEquals($result, ['class' => ['current', 'new_class']]);
// Test merging two arrays
$result = $this->template->mergeClasses(['class1'], ['class2']);
$this->assertEquals(['class1', 'class2'], $result);

// Test merging strings
$result = $this->template->mergeClasses('class1', 'class2');
$this->assertEquals(['class1', 'class2'], $result);

// Test merging space-separated strings
$result = $this->template->mergeClasses('class1 class2', 'class3 class4');
$this->assertEquals(['class1', 'class2', 'class3', 'class4'], $result);

// Test merging array with string
$result = $this->template->mergeClasses(['class1'], 'class2 class3');
$this->assertEquals(['class1', 'class2', 'class3'], $result);

// Test merging string with array
$result = $this->template->mergeClasses('class1 class2', ['class3']);
$this->assertEquals(['class1', 'class2', 'class3'], $result);

// Test uniqueness
$result = $this->template->mergeClasses(
['class1', 'class2', 'class1'],
['class2', 'class3'],
);
$this->assertEquals(['class1', 'class2', 'class3'], $result);

$result = $this->template->addClass('', 'new_class');
$this->assertEquals($result, ['class' => ['new_class']]);
// Test with empty strings
$result = $this->template->mergeClasses('', 'class1');
$this->assertEquals(['class1'], $result);

$result = $this->template->addClass(null, 'new_class');
$this->assertEquals($result, ['class' => ['new_class']]);
$result = $this->template->mergeClasses('class1', '');
$this->assertEquals(['class1'], $result);

$result = $this->template->addClass(false, 'new_class');
$this->assertEquals($result, ['class' => ['new_class']]);
$result = $this->template->mergeClasses('', '');
$this->assertEquals([], $result);

$result = $this->template->addClass(new stdClass(), 'new_class');
$this->assertEquals($result, ['class' => ['new_class']]);
}
// Test with empty arrays
$result = $this->template->mergeClasses([], ['class1']);
$this->assertEquals(['class1'], $result);

/**
* Test addClass method string parameter, it should fallback to string
*/
public function testAddClassMethodFallbackToString(): void
{
$result = $this->template->addClass('current', 'new_class');
$this->assertEquals($result, ['class' => ['current', 'new_class']]);
}
$result = $this->template->mergeClasses(['class1'], []);
$this->assertEquals(['class1'], $result);

/**
* Test addClass method to make sure the returned array is unique
*/
public function testAddClassMethodUnique(): void
{
$result = $this->template->addClass(['class' => ['new_class']], 'new_class');
$this->assertEquals($result, ['class' => ['new_class']]);
}
$result = $this->template->mergeClasses([], []);
$this->assertEquals([], $result);

/**
* Test addClass method useIndex param
*
* Tests for useIndex being the default, 'my_class' and false
*/
public function testAddClassMethodUseIndex(): void
{
$result = $this->template->addClass(
[
'class' => 'current_class',
'other_index1' => false,
'type' => 'text',
],
'new_class',
'class',
// Test preserving numeric indices (array_values ensures clean numeric keys)
$result = $this->template->mergeClasses(
['a', 'b', 'c'],
['b', 'd'],
);
$this->assertEquals($result, [
'class' => ['current_class', 'new_class'],
'other_index1' => false,
'type' => 'text',
]);

$result = $this->template->addClass(
[
'my_class' => 'current_class',
'other_index1' => false,
'type' => 'text',
],
'new_class',
'my_class',
);
$this->assertEquals($result, [
'other_index1' => false,
'type' => 'text',
'my_class' => ['current_class', 'new_class'],
]);

$result = $this->template->addClass(
[
'class' => [
'current_class',
'text',
],
],
'new_class',
'nonexistent',
);
$this->assertEquals($result, [
'class' => [
'current_class',
'text',
],
'nonexistent' => ['new_class'],
]);
$this->assertEquals(['a', 'b', 'c', 'd'], $result);
$this->assertSame([0, 1, 2, 3], array_keys($result));
}
}