diff --git a/core/includes/form.inc b/core/includes/form.inc index 90d47fb..652d7bc 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -1829,6 +1829,7 @@ function form_builder($form_id, &$element, &$form_state) { '#required' => FALSE, '#attributes' => array(), '#title_display' => 'before', + '#description_display' => 'after', ); // Special handling if we're on the top level form element. @@ -4642,19 +4643,23 @@ function form_process_file($element) { * $form structure and set via form_builder()). * - form-disabled: Only set if the form element is #disabled. * - * In addition to the element itself, the DIV contains a label for the element - * based on the optional #title_display property, and an optional #description. + * In addition to the element itself, the DIV contains the element's label + * consisting of the #title property and an optional #required marker. It also + * contains an optional #description. While by default, the label is displayed + * before the element and the description right after the element, this can be + * modified by the optional #title_display and #description_display properties. * * The optional #title_display property can have these values: - * - before: The label is output before the element. This is the default. - * The label includes the #title and the required marker, if #required. - * - after: The label is output after the element. For example, this is used + * - before (default): The label is displayed before the element. The label + * includes the #title and the required marker, if #required. + * - after: The label is displayed after the element. For example, this is used * for radio and checkbox #type elements as set in system_element_info(). * If the #title is empty but the field is #required, the label will * contain only the required marker. - * - invisible: Labels are critical for screen readers to enable them to - * properly navigate through forms but can be visually distracting. This - * property hides the label for everyone except screen readers. + * - invisible: While the label isn't displayed by visual browsers, it is + * available to screen readers which need it for properly navigating through + * forms. It is also available to validation functions that might set an error + * on the field that failed validation. * - 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 @@ -4667,13 +4672,25 @@ function form_process_file($element) { * This can be useful in cases such as the password_confirm element, which * creates children elements that have their own labels and required markers, * but the parent element should have neither. Use this carefully because a - * field without an associated label can cause accessibility challenges. + * field without an associated label can cause accessibility challenges. In most + * cases where displaying a title would be visually distracting, it is strongly + * recommended to supply a title but render it invisible or as attribute. + * + * The optional #description_display property can have these values: + * - before: The description is output before the element. This may be used for + * elements that occupy very much vertical screen space. + * - after (default): The description is output after the element. + * - invisible: While the description isn't displayed by visual browsers, it is + * available to screen readers. + * + * If the #description property is not set, the description will not be output, + * regardless of the #description_display value. * * @param $variables * An associative array containing: * - element: An associative array containing the properties of the element. - * Properties used: #title, #title_display, #description, #id, #required, - * #children, #type, #name. + * Properties used: #title, #title_display, #description, + * #description_display, #id, #required, #children, #type, #name. * * @ingroup themeable */ @@ -4684,6 +4701,7 @@ function theme_form_element($variables) { // may not necessarily have been processed by form_builder(). $element += array( '#title_display' => 'before', + '#description_display' => 'after', ); // Take over any #wrapper_attributes defined by the element. @@ -4710,41 +4728,36 @@ function theme_form_element($variables) { } $output = '' . "\n"; - // If #title is not set, we don't display any label or required marker. + // If #title is not set, we don't even call theme_form_element_label. if (!isset($element['#title'])) { $element['#title_display'] = 'none'; } - $prefix = isset($element['#field_prefix']) ? '' . $element['#field_prefix'] . ' ' : ''; - $suffix = isset($element['#field_suffix']) ? ' ' . $element['#field_suffix'] . '' : ''; + // If #description is not set, we don't even call + // theme_form_element_description. + if (!isset($element['#description'])) { + $element['#description_display'] == 'none'; + } - switch ($element['#title_display']) { - case 'before': - case 'invisible': - $output .= ' ' . theme('form_element_label', $variables); - $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n"; - break; + if ($element['#title_display'] == 'before' || $element['#title_display'] == 'invisible') { + $output .= ' ' . theme('form_element_label', $variables); + } + if ($element['#description_display'] == 'before') { + $output .= "\n" . theme('form_element_description', $variables) . "\n"; + } - case 'after': - $output .= ' ' . $prefix . $element['#children'] . $suffix; - $output .= ' ' . theme('form_element_label', $variables) . "\n"; - break; + // Render the element's children. + $output .= isset($element['#field_prefix']) ? '' . $element['#field_prefix'] . ' ' : ' '; + $output .= $element['#children']; + $output .= isset($element['#field_suffix']) ? ' ' . $element['#field_suffix'] . '' : ' '; - case 'none': - case 'attribute': - // Output no label and no required marker, only the children. - $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n"; - break; + if ($element['#title_display'] == 'after') { + $output .= ' ' . theme('form_element_label', $variables); } - - if (!empty($element['#description'])) { - $attributes = array('class' => 'description'); - if (!empty($element['#id'])) { - $attributes['id'] = $element['#id'] . '--description'; - } - $output .= '' . $element['#description'] . "\n"; + if ($element['#description_display'] == 'after' || $element['#description_display'] == 'invisible') { + $output .= "\n" . theme('form_element_description', $variables) . "\n"; } - $output .= "\n"; + $output .= '' . "\n"; return $output; } @@ -4787,7 +4800,7 @@ function theme_form_required_marker($variables) { * @param $variables * An associative array containing: * - element: An associative array containing the properties of the element. - * Properties used: #required, #title, #id, #value, #description. + * Properties used: #id, #title, #title_display, #required. * * @ingroup themeable */ @@ -4824,6 +4837,45 @@ function theme_form_element_label($variables) { } /** + * Returns HTML for a form element description. + * + * The description is associated with the element itself by the element #id. + * Labels may appear before or after elements, depending on theme_form_element() + * and #description_display. + * + * This function will not be called for elements with no #description. + * + * @param $variables + * An associative array containing: + * - element: An associative array containing the properties of the element. + * Properties used: #id, #description, #description_display. + * + * @ingroup themeable + */ +function theme_form_element_description($variables) { + $element = $variables['element']; + // This is also used in the installer, pre-database setup. + $t = get_t(); + + // If description is empty, don't output anything. + if (!isset($element['#description']) || $element['#description'] === '') { + return ''; + } + + $attributes = array(); + if ($element['#description_display'] == 'invisible') { + $attributes['class'] = 'element-invisible'; + } + else { + $attributes['class'] = 'description'; + } + if (!empty($element['#id'])) { + $attributes['id'] = $element['#id'] . '--description'; + } + return '' . $t($element['#description']) . ''; +} + +/** * Sets a form element's class attribute. * * Adds 'required' and 'error' classes as needed. diff --git a/core/includes/theme.inc b/core/includes/theme.inc index ae8ec15..e1e7fd5 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -3294,6 +3294,9 @@ function drupal_common_theme() { 'form_element_label' => array( 'render element' => 'element', ), + 'form_element_description' => array( + 'render element' => 'element', + ), 'vertical_tabs' => array( 'render element' => 'element', ), diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php index e64db93..6169ee8 100644 --- a/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -187,6 +187,7 @@ public function form(array $form, array &$form_state) { '#default_value' => !empty($node->name) ? $node->name : '', '#weight' => -1, '#description' => t('Leave blank for %anonymous.', array('%anonymous' => $user_config->get('anonymous'))), + '#description_display' => 'invisible', ); $form['author']['date'] = array( '#type' => 'textfield', diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 30ee8b7..336ad2d 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -287,6 +287,8 @@ function hook_queue_info_alter(&$queues) { * - "#submit": array of callback functions taking $form and $form_state. * - "#title_display": optional string indicating if and how #title should be * displayed, see theme_form_element() and theme_form_element_label(). + * - "#description_display": optional string indicating how the #description + * should be displayed, see theme_form_element(). * * @see hook_element_info_alter() * @see system_element_info()