Summary
A new form element property #states_attributes_target_key has been introduced to the Form API to explicitly control where the attributes generated for a #states property are placed in the render array (for example, #attributes vs #wrapper_attributes). This provides a more reliable mechanism for specifying the target attribute key for states data than the existing internal heuristics approach that is based on element type and properties.
Before
Prior to this change, the Form API’s FormHelper::processStates() determined where to attach #states attributes (data-drupal-states) based on an internal conditional that examined element properties such as #type, #markup, and #input. Unfortunately this internal heuristic approach has proven unreliable under certain circumstances.
For example, in some cases (e.g., #type => 'item' with non-empty #markup) #states attributes were applied to the wrong attribute key, causing states to not work as expected on those elements. This regression was introduced alongside changes in issue #1427838: password_confirm children do not pick up #states or #attributes and led to incorrect placement of #states attributes for certain render elements.
After
With this change, developers can set the new element property when defining their form element:
#[FormElement('item')]
class Item extends FormElementBase {
/**
* {@inheritdoc}
*/
public function getInfo() {
return [
'#input' => TRUE,
'#markup' => '',
'#theme_wrappers' => ['form_element'],
// We want our #states information attached to our wrapper.
'#states_attributes_target_key' => '#wrapper_attributes',
];
}
to explicitly control where data-drupal-states is placed on the element. If the property is not provided, core will default to '#attributes'. This makes the behavior predictable across all element types including item, container, and composite elements with child inputs.
Why this matters
Modules and themes that attach #states to form elements that are not actual input controls (such as #type => 'item' with HTML content) have experienced broken or inconsistent states behavior due to where the states data was being added in the render structure. With this explicit API, maintainers can avoid bugs caused by internal heuristics and ensure predictable behavior when manipulating form element states in complex forms.