Skip to content

Commit

Permalink
Add labelOptions for multi-checkbox/radio
Browse files Browse the repository at this point in the history
Merge branch 'issue-10138' into master, enabling multi-checkbox and
radio input sets to have additional attributes defined on the label
elements through the `labelOptions` key.
  • Loading branch information
markstory committed Feb 13, 2017
2 parents 441e44b + b0a3fe8 commit b138621
Show file tree
Hide file tree
Showing 6 changed files with 482 additions and 21 deletions.
28 changes: 22 additions & 6 deletions src/View/Helper/FormHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,9 @@ public function fieldset($fields = '', array $options = [])
* - `templates` - The templates you want to use for this input. Any templates will be merged on top of
* the already loaded templates. This option can either be a filename in /config that contains
* the templates you want to load, or an array of templates to use.
* - `labelOptions` - Either `false` to disable label around nestedWidgets e.g. radio, multicheckbox or an array
* of attributes for the label tag. `selected` will be added to any classes e.g. `class => 'myclass'` where
* widget is checked
*
* @param string $fieldName This should be "modelname.fieldname"
* @param array $options Each type of input takes different options.
Expand All @@ -1008,7 +1011,8 @@ public function input($fieldName, array $options = [])
'required' => null,
'options' => null,
'templates' => [],
'templateVars' => []
'templateVars' => [],
'labelOptions' => true
];
$options = $this->_parseOptions($fieldName, $options);
$options += ['id' => $this->_domId($fieldName)];
Expand Down Expand Up @@ -1038,6 +1042,9 @@ public function input($fieldName, array $options = [])
$label = $options['label'];
unset($options['label']);

$labelOptions = $options['labelOptions'];
unset($options['labelOptions']);

$nestedInput = false;
if ($options['type'] === 'checkbox') {
$nestedInput = true;
Expand All @@ -1049,7 +1056,7 @@ public function input($fieldName, array $options = [])
$options['hiddenField'] = '_split';
}

$input = $this->_getInput($fieldName, $options);
$input = $this->_getInput($fieldName, $options + ['labelOptions' => $labelOptions]);
if ($options['type'] === 'hidden' || $options['type'] === 'submit') {
if ($newTemplates) {
$templater->pop();
Expand Down Expand Up @@ -1130,22 +1137,24 @@ protected function _inputContainerTemplate($options)
*/
protected function _getInput($fieldName, $options)
{
$label = $options['labelOptions'];
unset($options['labelOptions']);
switch (strtolower($options['type'])) {
case 'select':
$opts = $options['options'];
unset($options['options']);

return $this->select($fieldName, $opts, $options);
return $this->select($fieldName, $opts, $options + ['label' => $label]);
case 'radio':
$opts = $options['options'];
unset($options['options']);

return $this->radio($fieldName, $opts, $options);
return $this->radio($fieldName, $opts, $options + ['label' => $label]);
case 'multicheckbox':
$opts = $options['options'];
unset($options['options']);

return $this->multiCheckbox($fieldName, $opts, $options);
return $this->multiCheckbox($fieldName, $opts, $options + ['label' => $label]);
case 'input':
throw new RuntimeException("Invalid type 'input' used for field '$fieldName'");

Expand Down Expand Up @@ -1463,7 +1472,9 @@ public function checkbox($fieldName, array $options = [])
* ### Attributes:
*
* - `value` - Indicates the value when this radio button is checked.
* - `label` - boolean to indicate whether or not labels for widgets should be displayed.
* - `label` - Either `false` to disable label around the widget or an array of attributes for
* the label tag. `selected` will be added to any classes e.g. `'class' => 'myclass'` where widget
* is checked
* - `hiddenField` - boolean to indicate if you want the results of radio() to include
* a hidden input with a value of ''. This is useful for creating radio sets that are non-continuous.
* - `disabled` - Set to `true` or `disabled` to disable all the radio buttons.
Expand Down Expand Up @@ -1930,6 +1941,8 @@ public function select($fieldName, $options = [], array $attributes = [])
return $this->multiCheckbox($fieldName, $options, $attributes);
}

unset($attributes['label']);

// Secure the field if there are options, or it's a multi select.
// Single selects with no options don't submit, but multiselects do.
if ($attributes['secure'] &&
Expand Down Expand Up @@ -1970,6 +1983,9 @@ public function select($fieldName, $options = [], array $attributes = [])
* You can also set disabled to a list of values you want to disable when creating checkboxes.
* - `hiddenField` - Set to false to remove the hidden field that ensures a value
* is always submitted.
* - `label` - Either `false` to disable label around the widget or an array of attributes for
* the label tag. `selected` will be added to any classes e.g. `'class' => 'myclass'` where
* widget is checked
*
* Can be used in place of a select box with the multiple attribute.
*
Expand Down
43 changes: 43 additions & 0 deletions src/View/StringTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Core\InstanceConfigTrait;
use Cake\Utility\Hash;
use RuntimeException;

/**
Expand Down Expand Up @@ -323,4 +324,46 @@ protected function _formatAttribute($key, $value, $escape = true)

return $key . '="' . ($escape ? h($value) : $value) . '"';
}

/**
* Adds a class and returns a unique list either in array or space separated
*
* @param array|string $input The array or string to add the class to
* @param array|string $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
*/
public function addClass($input, $newClass, $useIndex = 'class')
{
// NOOP
if (empty($newClass)) {
return $input;
}

if (is_array($input)) {
$class = Hash::get($input, $useIndex, []);
} else {
$class = $input;
$input = [];
}

// Convert and sanitise the inputs
if (!is_array($class)) {
if (is_string($class) && !empty($class)) {
$class = explode(' ', $class);
} else {
$class = [];
}
}

if (is_string($newClass)) {
$newClass = explode(' ', $newClass);
}

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

$input = Hash::insert($input, $useIndex, $class);

return $input;
}
}
41 changes: 26 additions & 15 deletions src/View/Widget/MultiCheckboxWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ public function render(array $data, ContextInterface $context)
'disabled' => null,
'val' => null,
'idPrefix' => null,
'templateVars' => []
'templateVars' => [],
'label' => true
];
$this->_idPrefix = $data['idPrefix'];
$this->_clearIds();
Expand Down Expand Up @@ -152,15 +153,15 @@ protected function _renderInputs($data, $context)
if (!isset($checkbox['templateVars'])) {
$checkbox['templateVars'] = $data['templateVars'];
}
if (!isset($checkbox['label'])) {
$checkbox['label'] = $data['label'];
}
if (!empty($data['templateVars'])) {
$checkbox['templateVars'] = array_merge($data['templateVars'], $checkbox['templateVars']);
}
$checkbox['name'] = $data['name'];
$checkbox['escape'] = $data['escape'];

if ($this->_isSelected($checkbox['value'], $data['val'])) {
$checkbox['checked'] = true;
}
$checkbox['checked'] = $this->_isSelected($checkbox['value'], $data['val']);
$checkbox['disabled'] = $this->_isDisabled($checkbox['value'], $data['disabled']);
if (empty($checkbox['id'])) {
$checkbox['id'] = $this->_id($checkbox['name'], $checkbox['value']);
Expand Down Expand Up @@ -190,17 +191,27 @@ protected function _renderInput($checkbox, $context)
)
]);

$labelAttrs = [
'for' => $checkbox['id'],
'escape' => $checkbox['escape'],
'text' => $checkbox['text'],
'templateVars' => $checkbox['templateVars'],
'input' => $input,
];
if (!empty($checkbox['checked']) && empty($labelAttrs['class'])) {
$labelAttrs['class'] = 'selected';
if ($checkbox['label'] === false && strpos($this->_templates->get('radioWrapper'), '{{input}}') === false) {
$label = $input;
} else {
$labelAttrs = [
'for' => $checkbox['id'],
'escape' => $checkbox['escape'],
'text' => $checkbox['text'],
'templateVars' => $checkbox['templateVars'],
'input' => $input
];

if (is_array($checkbox['label'])) {
$labelAttrs += $checkbox['label'];
}

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

$label = $this->_label->render($labelAttrs, $context);
}
$label = $this->_label->render($labelAttrs, $context);

return $this->_templates->format('checkboxWrapper', [
'templateVars' => $checkbox['templateVars'],
Expand Down
5 changes: 5 additions & 0 deletions src/View/Widget/RadioWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ protected function _renderInput($val, $text, $data, $context)
if (isset($data['val']) && (string)$data['val'] === (string)$radio['value']) {
$radio['checked'] = true;
}

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

$radio['disabled'] = $this->_isDisabled($radio, $data['disabled']);
if (!empty($data['required'])) {
$radio['required'] = true;
Expand Down

0 comments on commit b138621

Please sign in to comment.