JSON serialzation of nested, single element broken

Created on 11 September 2024, 10 months ago
Updated 12 September 2024, 10 months ago

Problem

Nested custom elements are now generated with another layer of nesting. It happens with the response of the /user/login route of lupus_decoupled_user_forms

Before 2.x:
"content": "<div class=\"js-form-item form-item js-form-type-textfield form-item-name js-form-item-name\">\n <label for=\"edit-name\" class=\"form-item__label js-form-required form-required\">Username</label>\n <input autocorrect=\"none\" autocapitalize=\"none\" spellcheck=\"false\" autofocus=\"autofocus\" autocomplete=\"username\" data-drupal-selector=\"edit-name\" type=\"text\" id=\"edit-name\" name=\"name\" value=\"\" size=\"60\" maxlength=\"60\" class=\"form-text required form-element form-element--type-text form-element--api-textfield\" required=\"required\" aria-required=\"true\" />\n\n </div>\n<div class=\"js-form-item form-item js-form-type-password form-item-pass js-form-item-pass\">\n <label for=\"edit-pass\" class=\"form-item__label js-form-required form-required\">Password</label>\n <input autocomplete=\"current-password\" data-drupal-selector=\"edit-pass\" type=\"password\" id=\"edit-pass\" name=\"pass\" size=\"60\" maxlength=\"128\" class=\"form-text required form-element form-element--type-password form-element--api-password\" required=\"required\" aria-required=\"true\" />\n\n </div>\n<input data-drupal-selector=\"form-gwskh4lhqjtzrohz2tay1-rc3fqnwdvnwawisiwckqu\" type=\"hidden\" name=\"form_build_id\" value=\"form-GWSkh4lhQJtZROHz2tAy1_RC3FqNwdVnWawisIwCkqU\" />\n<input data-drupal-selector=\"edit-user-login-form\" type=\"hidden\" name=\"form_id\" value=\"user_login_form\" />\n<div data-drupal-selector=\"edit-actions\" class=\"form-actions js-form-wrapper form-wrapper\" id=\"edit-actions\"><input class=\"button--primary button js-form-submit form-submit\" data-drupal-selector=\"edit-submit\" type=\"submit\" id=\"edit-submit\" name=\"op\" value=\"Log in\" />\n</div>\n"

After 3.x:

content": {

          "content": "<div class=\"js-form-item form-item js-form-type-textfield form-item-name js-form-item-name\">\n      <label for=\"edit-name\" class=\"form-item__label js-form-required form-required\">Username</label>\n        <input autocorrect=\"none\" autocapitalize=\"none\" spellcheck=\"false\" autofocus=\"autofocus\" autocomplete=\"username\" data-drupal-selector=\"edit-name\" type=\"text\" id=\"edit-name\" name=\"name\" value=\"\" size=\"60\" maxlength=\"60\" class=\"form-text required form-element form-element--type-text form-element--api-textfield\" required=\"required\" aria-required=\"true\" />\n\n        </div>\n<div class=\"js-form-item form-item js-form-type-password form-item-pass js-form-item-pass\">\n      <label for=\"edit-pass\" class=\"form-item__label js-form-required form-required\">Password</label>\n        <input autocomplete=\"current-password\" data-drupal-selector=\"edit-pass\" type=\"password\" id=\"edit-pass\" name=\"pass\" size=\"60\" maxlength=\"128\" class=\"form-text required form-element form-element--type-password form-element--api-password\" required=\"required\" aria-required=\"true\" />\n\n        </div>\n<input data-drupal-selector=\"form-qd1-cga5hjehlf-c-yo1c66ajunkmc-3ibqqvfdweeo\" type=\"hidden\" name=\"form_build_id\" value=\"form-QD1_cGA5hJEHlF_c_Yo1C66AjUnKMc-3ibqQVfdweeo\" />\n<input data-drupal-selector=\"edit-user-login-form\" type=\"hidden\" name=\"form_id\" value=\"user_login_form\" />\n<div data-drupal-selector=\"edit-actions\" class=\"form-actions js-form-wrapper form-wrapper\" id=\"edit-actions\"><input class=\"button--primary button js-form-submit form-submit\" data-drupal-selector=\"edit-submit\" type=\"submit\" id=\"edit-submit\" name=\"op\" value=\"Log in\" />\n</div>\n"

    }

Proposed resolution

I suspect this got caused by the removal of the single slot normalization, which was auto-enabled before in some situation. Once we make sure those un-named wrapping elements are "drupal-markup" things might work anyway. However, it would be better to avoid this unnecessary level of nesting anyway. So let's better fix this to keep this normalized nicely for this case.

🐛 Bug report
Status

Closed: duplicate

Version

3.0

Component

Code

Created by

🇦🇹Austria fago Vienna

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @fago
  • 🇦🇹Austria fago Vienna

    after taking a closer look, the problem is not a missing element name, but not handling a markup-only slot correctly. There is simply no custom element to wrap it. The markup serialization has it well:

          if ($slot_entry['content'] instanceof CustomElement) {
            $element = $slot_entry['content'];
            $render[] = $element->toRenderArray();
          }
          else {
            $render[] = [
              '#markup' => $slot_entry['content'],
            ];
          }
    

    result is also good:
    "content": "<drupal-form-user-login-form form-id=\"user_login_form\" attributes=\"{&quot;class&quot;:[&quot;user-login-form&quot;],&quot;data-drupal-selector&quot;:&quot;user-login-form&quot;}\" method=\"post\"><div class=\"js-form-item form-item js-form-type-textfield form-type--textfield js-form-item-name form-item--name\">\n <label for=\"edit-name\" class=\"form-item__label js-form-required form-required\">Username</label>\n <input autocorrect=\"none\" autocapitalize=\"none\" spellcheck=\"false\" autofocus=\"autofocus\" autocomplete=\"username\" data-drupal-selector=\"edit-name\" type=\"text\" id=\"edit-name\" name=\"name\" value=\"\" size=\"60\" maxlength=\"60\" class=\"form-text required form-element form-element--type-text form-element--api-textfield\" required=\"required\" aria-required=\"true\"/>\n </div>\n<div class=\"js-form-item form-item js-form-type-password form-type--password js-form-item-pass form-item--pass\">\n <label for=\"edit-pass\" class=\"form-item__label js-form-required form-required\">Password</label>\n <input autocomplete=\"current-password\" data-drupal-selector=\"edit-pass\" type=\"password\" id=\"edit-pass\" name=\"pass\" size=\"60\" maxlength=\"128\" class=\"form-text required form-element form-element--type-password form-element--api-password\" required=\"required\" aria-required=\"true\"/>\n </div>\n<input autocomplete=\"off\" data-drupal-selector=\"form-lgt9tugfanomw-fvhtaxhqogonsveqfilbf-zsuu8a\" type=\"hidden\" name=\"form_build_id\" value=\"form-_lgt9TuGfaNOmW-fVHTaxHqogONsvEqfiLBf-Zsuu8A\"/><input data-drupal-selector=\"edit-user-login-form\" type=\"hidden\" name=\"form_id\" value=\"user_login_form\"/><div data-drupal-selector=\"edit-actions\" class=\"form-actions js-form-wrapper form-wrapper\" id=\"edit-actions\"><input data-drupal-selector=\"edit-submit\" type=\"submit\" id=\"edit-submit\" name=\"op\" value=\"Log in\" class=\"button js-form-submit form-submit\"/></div>\n</drupal-form-user-login-form>\n",

    So we need to make sure the JSON version of this stays right.

    While looking at the code, I figrued it's weird that we default to "div" as custom element tag, since that is not a custom element at all. So I did a quick MR to change the default to drupal-markup.

  • Status changed to Needs review 10 months ago
  • 🇦🇹Austria fago Vienna

    attached MR fixes the problem.

    Having markup-only slot + attributes is not a valid case, which is handled by CustomElement::setSlot(). So the normalizer does not need to check for that. So we can safely remove this use-less nested 'content' key here and simplify the code.

  • Status changed to Closed: duplicate 10 months ago
  • 🇦🇹Austria fago Vienna

    I accidentially attached the MRs to the wrong issue 🐛 SON serialization of markup only slots is broken Needs review - but they are actually identical. Both issues are in the end the broken markup serialization. So let'S handle this over there.

Production build 0.71.5 2024