JSON serialzation of nested, single element broken

Created on 11 September 2024, 4 months ago
Updated 12 September 2024, 4 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 4 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 4 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