';
+ $output .= $prefix . '
\n";
return $output;
}
@@ -1241,11 +1252,15 @@ function theme_checkboxes($variables) {
}
/**
- * Adds form element theming to an element if its title or description is set.
+ * Adds theming to composite form elements such as checkboxes and radios.
*
- * This is used as a pre render function for checkboxes and radios.
+ * If either the title or description is set for the composite element as a * whole, then this adds either a 'fieldset' or 'form_element' theme wrapper to
+ * render them. A fieldset is generally preferred to assist screen readers
+ * (http://webaim.org/techniques/forms/screen_reader#group), but is not used
+ * when #title_display is set to an option impossible to render as a fieldset
+ * legend.
*/
-function form_pre_render_conditional_form_element($element) {
+function form_pre_render_composite_form_element($element) {
// Set the element's title attribute to show #title as a tooltip, if needed.
if (isset($element['#title']) && $element['#title_display'] == 'attribute') {
$element['#attributes']['title'] = $element['#title'];
@@ -1256,7 +1271,16 @@ function form_pre_render_conditional_form_element($element) {
}
if (isset($element['#title']) || isset($element['#description'])) {
- $element['#theme_wrappers'][] = 'form_element';
+ // To assist screen readers, use a fieldset wrapper when possible, but
+ // fieldset legends are only compatible with the 'before' and 'invisible'
+ // options for #title_display.
+ if (!isset($element['#title_display']) || in_array($element['#title_display'], array('before', 'invisible'))) {
+ $wrapper = 'fieldset';
+ }
+ else {
+ $wrapper = 'form_element';
+ }
+ $element['#theme_wrappers'][] = $wrapper;
}
return $element;
}
@@ -2713,7 +2737,7 @@ function form_process_file($element) {
* property hides the label for everyone except screen readers.
* - attribute: Set the title attribute on the element to create a tooltip
* but output no label element. This is supported only for checkboxes
- * and radios in form_pre_render_conditional_form_element(). It is used
+ * and radios in form_pre_render_composite_form_element(). It is used
* where a visual label is not needed, such as a table of checkboxes where
* the row and column provide the context. The tooltip will include the
* title and required marker.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php
index 6c51a04..02bc1d9 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php
@@ -78,10 +78,10 @@ function testFormLabels() {
$this->assertFalse(isset($elements[0]), 'No label tag when title set not to display.');
// Check #field_prefix and #field_suffix placement.
- $elements = $this->xpath('//span[@class="field-prefix"]/following-sibling::div[@id="edit-form-radios-test"]');
+ $elements = $this->xpath('//span[@class="field-prefix"]/following-sibling::fieldset[@id="edit-form-radios-test"]');
$this->assertTrue(isset($elements[0]), 'Properly placed the #field_prefix element after the label and before the field.');
- $elements = $this->xpath('//span[@class="field-suffix"]/preceding-sibling::div[@id="edit-form-radios-test"]');
+ $elements = $this->xpath('//span[@class="field-suffix"]/preceding-sibling::fieldset[@id="edit-form-radios-test"]');
$this->assertTrue(isset($elements[0]), 'Properly places the #field_suffix element immediately after the form field.');
// Check #prefix and #suffix placement.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
index 5424f66..172b2a1 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
@@ -94,7 +94,7 @@ function testRequiredFields() {
$elements['file']['empty_values'] = $empty_strings;
// Regular expression to find the expected marker on required elements.
- $required_marker_preg = '@
\*@';
+ $required_marker_preg = '@<(label|legend).*\*(label|legend)>@';
// Go through all the elements and all the empty values for them.
foreach ($elements as $type => $data) {
@@ -520,7 +520,7 @@ function testDisabledElements() {
// All the elements should be marked as disabled, including the ones below
// the disabled container.
- $this->assertEqual(count($disabled_elements), 39, 'The correct elements have the disabled property in the HTML code.');
+ $this->assertEqual(count($disabled_elements), 41, 'The correct elements have the disabled property in the HTML code.');
$this->drupalPostForm(NULL, $edit, t('Submit'));
$returned_values['hijacked'] = drupal_json_decode($this->content);
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 1657f9f..ec91d05 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -449,7 +449,7 @@ function system_element_info() {
'#input' => TRUE,
'#process' => array('form_process_radios'),
'#theme_wrappers' => array('radios'),
- '#pre_render' => array('form_pre_render_conditional_form_element'),
+ '#pre_render' => array('form_pre_render_composite_form_element'),
);
$types['radio'] = array(
'#input' => TRUE,
@@ -463,7 +463,7 @@ function system_element_info() {
$types['checkboxes'] = array(
'#input' => TRUE,
'#process' => array('form_process_checkboxes'),
- '#pre_render' => array('form_pre_render_conditional_form_element'),
+ '#pre_render' => array('form_pre_render_composite_form_element'),
'#theme_wrappers' => array('checkboxes'),
);
$types['checkbox'] = array(