diff --git a/core/includes/form.inc b/core/includes/form.inc
index 967b88c..eb68923 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -4720,35 +4720,42 @@ function theme_form_element($variables) {
if (!empty($element['#attributes']['disabled'])) {
$attributes['class'][] = 'form-disabled';
}
- $output = '
' . "\n";
+ $output = "\n" . '
' . "\n";
- // If #title is not set, we don't even call theme_form_element_label.
+ // If #title or #description is not set at all, unset the respective display
+ // property, so the theme layer isn't unnecessarily called.
if (!isset($element['#title'])) {
$element['#title_display'] = 'none';
}
- // If #description is not set, we don't even call
- // theme_form_element_description.
if (!isset($element['#description'])) {
$element['#description_display'] == 'none';
}
+ // Print #title and #decription if it should be displayed before the element.
if ($element['#title_display'] == 'before' || $element['#title_display'] == 'invisible') {
- $output .= ' ' . theme('form_element_label', $variables);
+ $output .= theme('form_element_label', $variables);
}
if ($element['#description_display'] == 'before') {
- $output .= "\n" . theme('form_element_description', $variables) . "\n";
+ $output .= theme('form_element_description', $variables);
}
+ // Prepend a field prefix if given.
+ if (isset($element['#field_prefix'])) {
+ $output .= ' ' . $element['#field_prefix'] . '' . "\n";
+ }
// Render the element's children.
- $output .= isset($element['#field_prefix']) ? '' . $element['#field_prefix'] . ' ' : ' ';
- $output .= $element['#children'];
- $output .= isset($element['#field_suffix']) ? ' ' . $element['#field_suffix'] . '' : ' ';
+ $output .= ' ' . $element['#children'] . "\n";
+ // Append a field suffix if given.
+ if (isset($element['#field_suffix'])) {
+ $output .= ' ' . $element['#field_suffix'] . '' . "\n";
+ }
+ // Print #title and #decription if it should be displayed after the element.
if ($element['#title_display'] == 'after') {
- $output .= ' ' . theme('form_element_label', $variables);
+ $output .= theme('form_element_label', $variables);
}
if ($element['#description_display'] == 'after' || $element['#description_display'] == 'invisible') {
- $output .= "\n" . theme('form_element_description', $variables) . "\n";
+ $output .= theme('form_element_description', $variables);
}
$output .= '
' . "\n";
@@ -4823,7 +4830,7 @@ function theme_form_element_label($variables) {
$attributes['for'] = $element['#id'];
}
- return '
';
+ return '
' . "\n";
}
/**
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 feb354b..8eb3578 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php
@@ -23,8 +23,8 @@ class ElementsLabelsTest extends WebTestBase {
public static function getInfo() {
return array(
- 'name' => 'Form element and label output test',
- 'description' => 'Test form element labels, required markers and associated output.',
+ 'name' => 'Form element label and description test',
+ 'description' => 'Test form element labels, required markers, descriptions and associated output.',
'group' => 'Form API',
);
}
@@ -97,4 +97,30 @@ function testFormLabels() {
$elements = $this->xpath('//div[@id="edit-form-radios-title-attribute"]');
$this->assertEqual($elements[0]['title'], 'Radios test' . ' (' . t('Required') . ')', 'Title attribute found.');
}
+
+ /**
+ * Test description of form elements with different placement options.
+ */
+ function testFormDescriptions() {
+ $test = $this->drupalGet('form_test/form-descriptions');
+
+ // Check #description placement with #description_display='after'.
+ $field_id = 'edit-form-textfield-test-description-after';
+ $description_id = $field_id . '--description';
+ $elements = $this->xpath('//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/following-sibling::div[@id="' . $description_id . '"]');
+ $this->assertTrue(isset($elements[0]), t('Properly places the #description element after the form item.'));
+
+ // Check #description placement with #description_display='before'.
+ $field_id = 'edit-form-textfield-test-description-before';
+ $description_id = $field_id . '--description';
+ $elements = $this->xpath('//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/preceding-sibling::div[@id="' . $description_id . '"]');
+ $this->assertTrue(isset($elements[0]), t('Properly places the #description element before the form item.'));
+
+ // Check #description placement with #description_display='invisible'.
+ $field_id = 'edit-form-textfield-test-description-invisible';
+ $description_id = $field_id . '--description';
+ $elements = $this->xpath('//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/following-sibling::div[@class="visually-hidden"]');
+ $this->assertTrue(isset($elements[0]), t('Properly renders the #description element visually-hidden.'));
+ }
+
}
diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module
index cb56e2b..5ff635a 100644
--- a/core/modules/system/tests/modules/form_test/form_test.module
+++ b/core/modules/system/tests/modules/form_test/form_test.module
@@ -261,6 +261,14 @@ function form_test_menu() {
'type' => MENU_CALLBACK,
);
+ $items['form_test/form-descriptions'] = array(
+ 'title' => 'Form description test',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('form_description_test_form'),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
$items['form-test/state-persist'] = array(
'title' => 'Form state persistence without storage',
'page callback' => 'drupal_get_form',
@@ -1063,6 +1071,34 @@ function form_label_test_form() {
}
/**
+ * A form for testing placement of form descriptions.
+ */
+function form_description_test_form() {
+ $form['form_textfield_test_description_before'] = array(
+ '#type' =>'textfield',
+ '#title' => t('Textfield test for description before element'),
+ '#description' => t('Textfield test for description before element'),
+ '#description_display' => 'before'
+ );
+
+ $form['form_textfield_test_description_after'] = array(
+ '#type' =>'textfield',
+ '#title' => t('Textfield test for description after element'),
+ '#description' => t('Textfield test for description after element'),
+ '#description_display' => 'after'
+ );
+
+ $form['form_textfield_test_description_invisible'] = array(
+ '#type' =>'textfield',
+ '#title' => t('Textfield test for visually-hidden description'),
+ '#description' => t('Textfield test for visually-hidden description'),
+ '#description_display' => 'invisible'
+ );
+
+ return $form;
+}
+
+/**
* Menu callback; Invokes a form builder function with a wrapper callback.
*/
function form_test_wrapper_callback($form_id) {