The #theme property is not effectively overriding the theme function when the #type = 'submit'.

The code below demonstrates the issue. I have defined two form items: a textfield and a submit button. Both reference a custom theme key, 'tap_button', which returns "theme function override" instead of meaningful form markup. The override works properly for the textfield, but not the button.


/**
 * Implements hook_menu().
 */
function tap_menu() {
  $items['tap'] = array(
    'title' => 'tap!',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('jason_form'),
    'access arguments' => array('access content'),
    'type' => MENU_SUGGESTED_ITEM,
  );

  return $items;
}

function jason_form() {
  return array(
    'name' => array(
      '#type' => 'textfield',
      '#theme' => 'tap_button',
    ),
    'submit' => array(
      '#type' => 'submit',
      '#value' => 'test',
      '#theme' => 'tap_button',
    ),
  );
}

/**
 * Implements hook_theme().
 */
function tap_theme($existing, $type, $theme, $path) {
  return array(
    'tap_button' => array(
      'render element' => 'element',
    ),
  );
}

function theme_tap_button($variables) {
  return 'theme function override';
}
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dozymoe’s picture

I think this is a bug.

First introduced here: Refactor drupal_render theming - docs.

Problem is #type:button uses theme_button() as a #theme_wrappers (see system_element_info()).

This would render <input type="submit">, and removed the children render-array the #theme_wrappers is supposed to wrap. <input> doesn't allow any child html element.

Description about #theme_wrappers from the originating issue:

...

  • 2) We need the possibility for an element to have two theme functions assigned, one that renders the children and puts them into a single string, and one that wraps something around that string. Classical example is a form with custom theming: The custom theming function renders the children and puts them into a string, while the wrapper wraps the form tags around it.

...

We need two properties, for example for custom themed forms:
By default, the #type form has just #theme_wrapper set, #theme is empty. #theme_wrapper adds the form markup around the rendered child elements of the form. Now we can set #theme on an individual form (e.g. via hook_form_alter or in the form definition), where we control how and where the individual children are printed (we use this on many forms in core). But because of #theme_wrapper, we don't have to deal with the form markup, this just gets wrapped around the result of #theme. Same goes for fieldsets, or for individual form items, which are first themed and then the form_element markup is wrapped around the form element (which finally makes theme_form_element overridable on a per-element-basis. This used to be a pain for themers.).

...

submit, button, image_button has <input> as their wrapper, while other elements have either <form>, <div>, or <fieldset>.

dozymoe’s picture

Status: Active » Needs review
FileSize
1.06 KB

And the patch (renamed 'theme_wrappers' to 'theme'), changed status to 'need review' to summon the test bot.