ENHANCEMENT Allowing custom attributes in (most) FormField implementations, which allows for HTML5 data attributes

This commit is contained in:
Ingo Schommer 2011-12-22 13:10:57 +01:00
parent c77f4e8421
commit b5421d9598
45 changed files with 407 additions and 137 deletions

View File

@ -63,6 +63,27 @@ not when simply using the CMS or developing other CMS functionality.
If you want to extend the CMS stylesheets for your own projects without SCSS,
please create a new CSS file and link it into the CMS via `[api:LeftAndMain::require_css()]`.
### FormField consistently adds classes to HTML elements ###
The [api:FormField] API has been refactored to use SilverStripe templates
for constructing the field HTML, as well as new accessors for HTML attributes.
This change makes the HTML a bit more predictable, but it also means that
you need to check any code (CSS, JavaScript, etc) relying on the old inconsistencies.
Particularly, CSS class names applied through [api:FormField->addExtraClass()]
and the "type" class are now consistently added to the container `<div>`
as well as the HTML form element itself.
:::html
Before (abbreviated):
<div class="field checkbox extraClass"...>
<input type="checkbox".../>
</div>
After (abbreviated):
<div class="field checkbox extraClass"...>
<input type="checkbox" class="checkbox extraClass".../>
</div>
### Restructured files and folders ###
In order to make the `sapphire` framework useable without the `cms` module,

View File

@ -22,8 +22,6 @@ class CheckboxField extends FormField {
return ($this->value) ? 1 : 0;
}
}
/**
* Returns a restricted field holder used within things like FieldGroups
*/
@ -35,6 +33,18 @@ class CheckboxField extends FormField {
return $result;
}
function getAttributes() {
$attrs = parent::getAttributes();
$attrs['value'] = 1;
return array_merge(
$attrs,
array(
'checked' => ($this->Value()) ? 'checked' : null,
'type' => 'checkbox',
)
);
}
/**
* Returns a readonly version of this field
*/

View File

@ -98,12 +98,9 @@ class CheckboxSetField extends OptionsetField {
}
$odd = 0;
$options = '';
$options = array();
if ($source == null) {
$source = array();
$options = "<li>No options available</li>";
}
if ($source == null) $source = array();
if($source) {
foreach($source as $value => $item) {
@ -122,7 +119,7 @@ class CheckboxSetField extends OptionsetField {
$options[] = new ArrayData(array(
'ID' => $itemID,
'Class' => $extraClass,
'Name' => $this->name,
'Name' => "{$this->name}[{$value}]",
'Value' => $value,
'Title' => $title,
'isChecked' => in_array($value, $items) || in_array($value, $this->defaultItems),
@ -283,6 +280,10 @@ class CheckboxSetField extends OptionsetField {
return $field;
}
function Type() {
return 'optionset checkboxset';
}
function ExtraOptions() {
return FormField::ExtraOptions();

View File

@ -83,15 +83,25 @@ class CompositeField extends FormField {
$this->children = $children;
}
function extraClasses() {
$classes = array('field', 'CompositeField', parent::extraClasses());
if($this->columnCount) $classes[] = 'multicolumn';
return implode(' ', $classes);
}
function getAttributes() {
return array_merge(
parent::getAttributes(),
array('tabindex' => null, 'type' => null, 'value' => null, 'type' => null)
);
}
/**
* Returns the fields nested inside another DIV
*/
function FieldHolder() {
$content = '';
$fs = $this->FieldList();
$idAtt = isset($this->id) ? " id=\"{$this->id}\"" : '';
$className = ($this->columnCount) ? "field CompositeField {$this->extraClass()} multicolumn" : "field CompositeField {$this->extraClass()}";
$content = "<div class=\"$className\"$idAtt>\n";
foreach($fs as $subfield) {
if($this->columnCount) {
$className = "column{$this->columnCount}";
@ -101,9 +111,8 @@ class CompositeField extends FormField {
$content .= "\n" . $subfield->FieldHolder() . "\n";
}
}
$content .= "</div>\n";
return $content;
$this->createTag('div', $this->getAttributes(), $content);
}
/**

View File

@ -22,14 +22,14 @@ class CountryDropdownField extends DropdownField {
function defaultToVisitorCountry($val) {
$this->defaultToVisitorCountry = $val;
}
function Field() {
function Value() {
$source = $this->getSource();
if($this->defaultToVisitorCountry && !$this->value || !isset($source[$this->value])) {
$this->value = ($vc = Geoip::visitor_country()) ? $vc : Geoip::get_default_country_code();
return ($vc = Geoip::visitor_country()) ? $vc : Geoip::get_default_country_code();
} else {
return $this->value;
}
return parent::Field();
}
}

View File

@ -9,6 +9,8 @@ class CreditCardField extends TextField {
function Field() {
$parts = explode("\n", chunk_split($this->value,4,"\n"));
$parts = array_pad($parts, 4, "");
// TODO Mark as disabled/readonly
$field = "<span id=\"{$this->name}_Holder\" class=\"creditCardField\">" .
"<input autocomplete=\"off\" name=\"{$this->name}[0]\" value=\"$parts[0]\" maxlength=\"4\"" . $this->getTabIndexHTML(0) . " /> - " .
"<input autocomplete=\"off\" name=\"{$this->name}[1]\" value=\"$parts[1]\" maxlength=\"4\"" . $this->getTabIndexHTML(1) . " /> - " .

View File

@ -27,6 +27,11 @@ class CurrencyField extends TextField {
return 0.00;
}
}
function Type() {
return 'currency text';
}
/**
* Create a new class for this field
*/

View File

@ -18,6 +18,15 @@ class DatalessField extends FormField {
* Always returns false.
*/
function hasData() { return false; }
function getAttributes() {
return array_merge(
parent::getAttributes(),
array(
'type' => 'hidden',
)
);
}
/**
* Returns the field's representation in the form.
@ -58,4 +67,8 @@ class DatalessField extends FormField {
return $this->allowHTML;
}
function Type() {
return 'readonly';
}
}

View File

@ -174,6 +174,10 @@ class DateField extends TextField {
return $html;
}
function Type() {
return 'date text';
}
/**
* Sets the internal value to ISO date format.

View File

@ -162,6 +162,13 @@ class DropdownField extends FormField {
return $this->customise($properties)->renderWith($this->getTemplate());
}
function getAttributes() {
return array_merge(
parent::getAttributes(),
array('type' => null)
);
}
/**
* @return boolean
*/
@ -230,12 +237,6 @@ class DropdownField extends FormField {
return $field;
}
function extraClass() {
$ret = parent::extraClass();
if($this->extraClass) $ret .= " $this->extraClass";
return $ret;
}
/**
* Set form being disabled
*/

View File

@ -6,6 +6,10 @@
*/
class EmailField extends TextField {
function Type() {
return 'email text';
}
function jsValidation() {
$formID = $this->form->FormName();
$error = _t('EmailField.VALIDATIONJS', 'Please enter an email address.');
@ -40,14 +44,6 @@ if(typeof fromAnOnBlur != 'undefined'){
JS;
}
/**
* Returns the field type - used by templates.
* @return string
*/
function Type() {
return 'text';
}
/**
* Validates for RFC 2822 compliant email adresses.
*

View File

@ -117,6 +117,13 @@ class FileField extends FormField {
return $this->customise($properties)->renderWith($this->getTemplate());
}
function getAttributes() {
return array_merge(
parent::getAttributes(),
array('type' => 'file')
);
}
public function saveInto(DataObject $record) {
if(!isset($_FILES[$this->name])) return false;
$fileClass = File::get_class_for_file_extension(pathinfo($_FILES[$this->name]['name'], PATHINFO_EXTENSION));

View File

@ -91,7 +91,22 @@ class FormAction extends FormField {
}
public function Type() {
return ($this->useButtonTag) ? 'button' : 'submit';
return 'action';
}
function getAttributes() {
return array_merge(
parent::getAttributes(),
array(
'disabled' => ($this->isReadonly() || $this->isDisabled()),
'value' => $this->Title(),
'type' => ($this->useButtonTag) ? null : 'submit'
)
);
}
function extraClass() {
return 'action ' . parent::extraClass();
}
/**

View File

@ -89,6 +89,12 @@ class FormField extends RequestHandler {
*/
protected $fieldHolderTemplate = 'FieldHolder';
/**
* @var array All attributes on the form field (not the field holder).
* Partially determined based on other instance properties, please use {@link getAttributes()}.
*/
protected $attributes = array();
/**
* Create a new field.
* @param name The internal field name, passed to forms.
@ -253,21 +259,22 @@ class FormField extends RequestHandler {
* @return String CSS-classnames
*/
function extraClass() {
$output = "";
if(is_array($this->extraClasses)) {
$output = " " . implode($this->extraClasses, " ");
}
$classes = array();
$classes[] = $this->Type();
if($this->extraClasses) $classes = array_merge($classes, array_values($this->extraClasses));
// Allow customization of label and field tag positioning
if(!$this->Title()) $output .= " nolabel";
if(!$this->Title()) $classes[] = "nolabel";
// Allow custom styling of any element in the container based
// on validation errors, e.g. red borders on input tags.
// CSS-Class needs to be different from the one rendered
// through {@link FieldHolder()}
if($this->Message()) $output .= " holder-" . $this->MessageType();
if($this->Message()) $classes[] .= "holder-" . $this->MessageType();
return $output;
return implode(' ', $classes);
}
/**
@ -288,6 +295,73 @@ class FormField extends RequestHandler {
if(isset($this->extraClasses) && array_key_exists($class, $this->extraClasses)) unset($this->extraClasses[$class]);
}
/**
* Set an HTML attribute on the field element, mostly an <input> tag.
*
* CAUTION Doesn't work on most fields which are composed of more than one HTML form field:
* AjaxUniqueTextField, CheckboxSetField, ComplexTableField, CompositeField, ConfirmedPasswordField, CountryDropdownField,
* CreditCardField, CurrencyField, DateField, DatetimeField, FieldGroup, GridField, HtmlEditorField,
* ImageField, ImageFormAction, InlineFormAction, ListBoxField, etc.
*
* @param String
* @param String
*/
function setAttribute($name, $value) {
$this->attributes[$name] = $value;
}
/**
* Get an HTML attribute defined by the field, or added through {@link setAttribute()}.
* Caution: Doesn't work on all fields, see {@link setAttribute()}.
*
* @return String
*/
function getAttribute($name) {
$attrs = $this->getAttributes();
return @$attrs[$name];
}
/**
* @return array
*/
function getAttributes() {
$attrs = array(
'type' => 'text',
'name' => $this->getName(),
'value' => $this->Value(),
'class' => $this->extraClass(),
'id' => $this->ID(),
'tabindex' => $this->getTabIndex(),
'disabled' => $this->isDisabled(),
);
return array_merge($attrs, $this->attributes);
}
/**
* @param Array Custom attributes to process. Falls back to {@link getAttributes()}.
* If at least one argument is passed as a string, all arguments act as excludes by name.
* @return String HTML attributes, ready for insertion into an HTML tag
*/
function getAttributesHTML($attrs = null) {
$exclude = (is_string($attrs)) ? func_get_args() : null;
if(!$attrs || is_string($attrs)) $attrs = $this->getAttributes();
// Remove empty
$attrs = array_filter((array)$attrs, create_function('$v', 'return ($v || $v === 0);')); ;
// Remove excluded
if($exclude) $attrs = array_diff_key($attrs, array_flip($exclude));
// Create markkup
$parts = array();
foreach($attrs as $name => $value) {
$parts[] = ($value === true) ? "{$name}=\"{$name}\"" : "{$name}=\"" . Convert::raw2att($value) . "\"";
}
return implode(' ', $parts);
}
/**
* Returns a version of a title suitable for insertion into an HTML attribute
*/
@ -555,15 +629,13 @@ class FormField extends RequestHandler {
/**
* Returns the field type - used by templates.
* The field type is the class name with the word Field dropped off the end, all lowercase.
* It's handy for assigning HTML classes.
* It's handy for assigning HTML classes. Doesn't signify the <input type> attribute,
* see {link getAttributes()}.
*
* @return string
*/
function Type() {
if(get_class($this) == 'FormField') {
return 'hidden';
} else {
return strtolower(ereg_replace('Field$', '', $this->class));
}
return strtolower(ereg_replace('Field$', '', $this->class));
}
/**

View File

@ -40,14 +40,7 @@
class GroupedDropdownField extends DropdownField {
function Field() {
// Initialisations
$options = '';
$classAttr = '';
if($extraClass = trim($this->extraClass())) {
$classAttr = "class=\"$extraClass\"";
}
foreach($this->getSource() as $value => $title) {
if(is_array($title)) {
$options .= "<optgroup label=\"$value\">";
@ -62,9 +55,7 @@ class GroupedDropdownField extends DropdownField {
}
}
$id = $this->id();
return "<select $classAttr name=\"$this->name\" id=\"$id\">$options</select>";
return $this->createTag('select', $this->getAttributes(), $options);
}
}

View File

@ -36,4 +36,18 @@ class HeaderField extends DatalessField {
return $this->headingLevel;
}
function getAttributes() {
return array_merge(
array(
'id' => $this->ID(),
'class' => $this->extraClass()
),
$this->attributes
);
}
function Type() {
return null;
}
}

View File

@ -22,6 +22,13 @@ class HiddenField extends FormField {
return true;
}
function getAttributes() {
return array_merge(
parent::getAttributes(),
array('type' => 'hidden')
);
}
static function create($name) {
return new HiddenField($name);
}

View File

@ -22,8 +22,6 @@ class HtmlEditorField extends TextareaField {
public function __construct($name, $title = null, $rows = 30, $cols = 20, $value = '', $form = null) {
parent::__construct($name, $title, $rows, $cols, $value, $form);
$this->addExtraClass('htmleditor');
self::include_js();
}
@ -47,18 +45,21 @@ class HtmlEditorField extends TextareaField {
return $this->createTag (
'textarea',
array (
'class' => $this->extraClass(),
'rows' => $this->rows,
'cols' => $this->cols,
'style' => 'width: 97%; height: ' . ($this->rows * 16) . 'px', // prevents horizontal scrollbars
'tinymce' => 'true',
'id' => $this->id(),
'name' => $this->name
),
$this->getAttributes(),
htmlentities($value->getContent(), ENT_COMPAT, 'UTF-8')
);
}
function getAttributes() {
return array_merge(
parent::getAttributes(),
array(
'tinymce' => 'true',
'style' => 'width: 97%; height: ' . ($this->rows * 16) . 'px', // prevents horizontal scrollbars
'value' => null,
)
);
}
public function saveInto($record) {
if($record->escapeTypeForField($this->name) != 'xml') {

View File

@ -60,18 +60,12 @@ class ListboxField extends DropdownField {
/**
* Returns a <select> tag containing all the appropriate <option> tags
*/
function Field() {
$size = '';
$multiple = '';
if($this->size) $size = "size=\"$this->size\"";
function Field($properties = array()) {
if($this->multiple) {
$multiple = "multiple=\"multiple\"";
$this->name .= '[]';
}
$options = "";
$options = array();
// We have an array of values
if(is_array($this->value)){
@ -86,18 +80,36 @@ class ListboxField extends DropdownField {
break;
}
}
$options .= "<option$selected value=\"$value\">$title</option>\n";
$options[] = new ArrayData(array(
'Title' => $title,
'Value' => $value,
'Selected' => $selected,
));
}
}else{
} else {
// Listbox was based a singlular value, so treat it like a dropdown.
foreach($this->getSource() as $value => $title) {
$selected = $value == $this->value ? " selected=\"selected\"" : "";
$options .= "<option$selected value=\"$value\">$title</option>";
$options[] = new ArrayData(array(
'Title' => $title,
'Value' => $value,
'Selected' => $selected,
));
}
}
$id = $this->id();
return "<select $size $multiple name=\"$this->name\" id=\"$id\">$options</select>";
$properties = array_merge($properties, array('Options' => new ArrayList($options)));
return $this->customise($properties)->renderWith($this->getTemplate());
}
function getAttributes() {
return array_merge(
parent::getAttributes(),
array(
'multiple' => $this->multiple,
'size' => $this->size
)
);
}
/**

View File

@ -13,6 +13,10 @@ class NumericField extends TextField{
return $html;
}
function Type() {
return 'numeric text';
}
function jsValidation() {
$formID = $this->form->FormName();

View File

@ -24,8 +24,11 @@ class PasswordField extends TextField {
}
function Type() {
return 'password';
function getAttributes() {
return array_merge(
parent::getAttributes(),
array('type' => 'password')
);
}
/**
@ -39,6 +42,10 @@ class PasswordField extends TextField {
$field->setReadonly(true);
return $field;
}
function Type() {
return 'text password';
}
}
?>

View File

@ -14,4 +14,23 @@ class ReadonlyField extends FormField {
function performReadonlyTransformation() {
return clone $this;
}
function Value() {
if($this->value) return $this->dontEscape ? $this->value : Convert::raw2xml($this->value);
else return '<i>(' . _t('FormField.NONE', 'none') . ')</i>';
}
function getAttributes() {
return array_merge(
parent::getAttributes(),
array(
'type' => 'hidden',
'value' => null,
)
);
}
function Type() {
return 'readonly';
}
}

View File

@ -7,8 +7,15 @@
*/
class ResetFormAction extends FormAction {
public function Type() {
return 'reset';
function getAttributes() {
return array_merge(
parent::getAttributes(),
array('type' => 'reset')
);
}
function Type() {
return 'resetformaction';
}
}

View File

@ -36,16 +36,14 @@ class TextField extends FormField {
return $this->maxLength;
}
function Field($properties = array()) {
$properties = array_merge(
$properties,
function getAttributes() {
return array_merge(
parent::getAttributes(),
array(
'MaxLength' => ($this->getMaxLength()) ? $this->getMaxLength() : null,
'Size' => ($this->getMaxLength()) ? min($this->getMaxLength(), 30) : null
'maxlength' => $this->getMaxLength(),
'size' => ($this->getMaxLength()) ? min($this->getMaxLength(), 30) : null
)
);
return parent::Field($properties);
}
function InternallyLabelledField() {

View File

@ -42,24 +42,16 @@ class TextareaField extends FormField {
parent::__construct($name, $title, $value, $form);
}
/**
* Create the <textarea> or <span> HTML tag with the
* attributes for this instance of TextareaField. This
* makes use of {@link FormField->createTag()} functionality.
*
* @return HTML code for the textarea OR span element
*/
function Field($properties = array()) {
$properties = array_merge(
$properties,
function getAttributes() {
return array_merge(
parent::getAttributes(),
array(
'Rows' => $this->rows,
'Cols' => $this->cols,
'Value' => htmlentities($this->value, ENT_COMPAT, 'UTF-8')
'rows' => $this->rows,
'cols' => $this->cols,
'value' => null,
'type' => null
)
);
return parent::Field($properties);
}
function getTemplate() {
@ -113,4 +105,8 @@ class TextareaField extends FormField {
function setColumns($cols) {
$this->cols = $cols;
}
function Value() {
return htmlentities($this->value, ENT_COMPAT, 'UTF-8');
}
}

View File

@ -72,6 +72,10 @@ class TimeField extends TextField {
return parent::Field();
}
function Type() {
return 'time text';
}
/**
* Sets the internal value to ISO date format.
*

View File

@ -164,12 +164,16 @@ class TreeDropdownField extends FormField {
$properties,
array(
'Title' => $title,
'Metadata' => ($metadata) ? Convert::raw2json($metadata) : null
'Metadata' => ($metadata) ? Convert::raw2att(Convert::raw2json($metadata)) : null
)
);
return $this->customise($properties)->renderWith('TreeDropdownField');
}
function extraClass() {
return implode(' ', array(parent::extraClass(), ($this->showSearch ? "searchable" : null)));
}
/**
* Get the whole tree of a part of the tree via an AJAX request.

View File

@ -1,5 +1,5 @@
<% if IsReadonly %>
<ul class="SelectionGroup$extraClass">
<ul class="SelectionGroup<% if extraClass %> $extraClass<% end_if %>">
<% control FieldSet %>
<% if Selected %>
<li$Selected>
@ -10,5 +10,5 @@
<% end_if %>
<% end_control %>
<% else %>
<ul class="SelectionGroup$extraClass"><% control FieldSet %><li$Selected>{$RadioButton}{$RadioLabel}{$FieldHolder}</li><% end_control %></ul>
<ul class="SelectionGroup<% if extraClass %> $extraClass<% end_if %>"><% control FieldSet %><li$Selected>{$RadioButton}{$RadioLabel}{$FieldHolder}</li><% end_control %></ul>
<% end_if %>

View File

@ -1 +1 @@
<input id="$ID" class="checkbox$extraClass" type="$Type" value="1" name="$Name"<% if TabIndex %> tabindex="$TabIndex"<% end_if %><% if isDisabled %> disabled<% end_if %><% if Value %> checked<% end_if %>>
<input $AttributesHTML>

View File

@ -1,4 +1,4 @@
<div id="$Name" class="field $Type<% if extraClass %>$extraClass<% end_if %>">
<div id="$Name" class="field<% if extraClass %> $extraClass<% end_if %>">
$Field
<label class="right" for="$ID">$Title</label>
<% if Message %><span class="message $MessageType">$messageBlock</span><% end_if %>

View File

@ -1,8 +1,12 @@
<ul id="$ID" class="optionset checkboxset$extraClass">
<% control Options %>
<li class="$Class">
<input id="$ID" class="checkbox" name="$Name" type="checkbox" value="$Value"<% if isChecked %> checked<% end_if %><% if isDisabled %> disabled<% end_if %>>
<label for="$ID">$Title</label>
</li>
<% end_control %>
<ul id="$ID" class="$extraClass">
<% if Options.Count %>
<% control Options %>
<li class="$Class">
<input id="$ID" class="checkbox" name="$Name" type="checkbox" value="$Value"<% if isChecked %> checked="checked"<% end_if %><% if isDisabled %> disabled="disabled"<% end_if %>>
<label for="$ID">$Title</label>
</li>
<% end_control %>
<% else %>
<li>No options available</li>
<% end_if %>
</ul>

View File

@ -1,4 +1,4 @@
<select id="$ID" class="dropdown$extraClass" name="$Name"<% if TabIndex %> tabindex="$TabIndex"<% end_if %><% if isDisabled %> disabled<% end_if %>>
<select $AttributesHTML>
<% control Options %>
<option value="$Value"<% if Selected %> selected<% end_if %>>$Title</option>
<% end_control %>

View File

@ -1,6 +1,8 @@
<div id="$Name" class="field $Type<% if extraClass %>$extraClass<% end_if %>">
<div id="$Name" class="field<% if extraClass %> $extraClass<% end_if %>">
<% if Title %><label class="left" for="$ID">$Title</label><% end_if %>
<div class="middleColumn">$Field</div>
<div class="middleColumn">
$Field
</div>
<% if RightTitle %><label class="right" for="$ID">$RightTitle</label><% end_if %>
<% if Message %><span class="message $MessageType">$Message</span><% end_if %>
</div>

View File

@ -1,2 +1,2 @@
<input id="$ID" class="file$extraClass" type="file" name="$Name"<% if TabIndex %> tabindex="$TabIndex"<% end_if %>>
<input $AttributesHTML>
<input type="hidden" name="MAX_FILE_SIZE" value="$MaxFileSize">

View File

@ -1,5 +1,5 @@
<% if UseButtonTag %>
<button id="$ID" class="action$extraClass" type="$Type" title="$Title" value="$Title" name="$Name"<% if TabIndex %> $TabIndex<% end_if %><% if isDisabled %> disabled<% end_if %>></button>
<button $AttributesHTML></button>
<% else %>
<input id="$ID" class="action$extraClass" type="$Type" title="$Title" value="$Title" name="$Name"<% if TabIndex %> $TabIndex<% end_if %><% if isDisabled %> disabled<% end_if %>>
<input $AttributesHTML>
<% end_if %>

View File

@ -1,2 +1,2 @@
<span id="$ID" class="field$extraClass">$NiceValue</span>
<hidden type="hidden" name="$Name" value="$Value"<% if TabIndex %> tabindex="$TabIndex"<% end_if %>>
<span id="$ID"<% if extraClass %> class="$extraClass"<% end_if %>>$Value</span>
<input $AttributesHTML>

View File

@ -1 +1 @@
<h$HeadingLevel id="$ID" class="header$extraClass">$Title</h$HeadingLevel>
<h$HeadingLevel $AttributesHTML>$Title</h$HeadingLevel>

View File

@ -1 +1 @@
<input class="hidden$extraClass" type="hidden" id="$ID" name="$Name" value="$Value">
<input $AttributesHTML>

View File

@ -1 +1 @@
<label id="$ID" class="label$extraClass">$Title</label>
<label id="$ID" class="$extraClass">$Title</label>

View File

@ -1,4 +1,4 @@
<ul id="$ID" class="optionset$extraClass">
<ul id="$ID" class="$extraClass">
<% control Options %>
<li class="$Class">
<input id="$ID" class="radio" name="$Name" type="radio" value="$Value"<% if isChecked %> checked<% end_if %><% if isDisabled %> disabled<% end_if %>>

View File

@ -1 +1 @@
<input id="$ID" class="text$extraClass" type="$Type" value="$Value" name="$Name"<% if TabIndex %> tabindex="$TabIndex"<% end_if %><% if MaxLength %> maxlength="$MaxLength"<% end_if %><% if Size %> size="$Size"<% end_if %><% if isDisabled %> disabled<% end_if %> />
<input $AttributesHTML />

View File

@ -1 +1 @@
<textarea id="$ID" class="textarea$extraClass" name="$Name" rows="$Rows" cols="$Cols"<% if isDisabled %> disabled<% end_if %>>$Value</textarea>
<textarea $AttributesHTML>$Value</textarea>

View File

@ -1,3 +1,3 @@
<div id="TreeDropdownField_$ID" class="TreeDropdownField single$extraClass<% if ShowSearch %> searchable<% end_if %>" data-url-tree="$Link(tree)" data-tile="$Title"<% if Metadata %> data-metadata="$Metadata"<% end_if %>>
<input id="$ID" type="hidden" name="$Name" value="$Value">
<div id="TreeDropdownField_$ID" class="TreeDropdownField single<% if extraClass %> $extraClass<% end_if %><% if ShowSearch %> searchable<% end_if %>" data-url-tree="$Link(tree)" data-title="$Title"<% if Metadata %> data-metadata="$Metadata"<% end_if %>>
<input id="$ID" type="hidden" name="$Name" value="$Value" />
</div>

View File

@ -4,6 +4,50 @@
* @subpackage tests
*/
class FormFieldTest extends SapphireTest {
function testAttributes() {
$field = new FormField('MyField');
$field->setAttribute('foo', 'bar');
$this->assertEquals('bar', $field->getAttribute('foo'));
$attrs = $field->getAttributes();
$this->assertArrayHasKey('foo', $attrs);
$this->assertEquals('bar', $attrs['foo']);
}
function testAttributesHTML() {
$field = new FormField('MyField');
$field->setAttribute('foo', 'bar');
$this->assertContains('foo="bar"', $field->getAttributesHTML());
$field->setAttribute('foo', null);
$this->assertNotContains('foo=', $field->getAttributesHTML());
$field->setAttribute('foo', '');
$this->assertNotContains('foo=', $field->getAttributesHTML());
$field->setAttribute('foo', false);
$this->assertNotContains('foo=', $field->getAttributesHTML());
$field->setAttribute('foo', true);
$this->assertContains('foo="foo"', $field->getAttributesHTML());
$field->setAttribute('foo', 'false');
$this->assertContains('foo="false"', $field->getAttributesHTML());
$field->setAttribute('foo', 'true');
$this->assertContains('foo="true"', $field->getAttributesHTML());
$field->setAttribute('foo', 0);
$this->assertContains('foo="0"', $field->getAttributesHTML());
$field->setAttribute('one', 1);
$field->setAttribute('two', 2);
$field->setAttribute('three', 3);
$this->assertNotContains('one="1"', $field->getAttributesHTML('one', 'two'));
$this->assertNotContains('two="2"', $field->getAttributesHTML('one', 'two'));
$this->assertContains('three="3"', $field->getAttributesHTML('one', 'two'));
}
function testEveryFieldTransformsReadonlyAsClone() {
$fieldClasses = ClassInfo::subclassesFor('FormField');

View File

@ -8,6 +8,6 @@ class LabelFieldTest extends SapphireTest {
function testFieldHasNoNameAttribute() {
$field = new LabelField('MyName', 'MyTitle');
$this->assertEquals($field->Field(), '<label id="MyName" class="label">MyTitle</label>');
$this->assertEquals($field->Field(), '<label id="MyName" class="readonly">MyTitle</label>');
}
}