Problem/Motivation

The checkboxes element fails on 2+ selections because of a submission-model mismatch between the NYSDS web component and Drupal's checkboxes element:

The NYSDS nys-checkboxgroup is a formAssociated web component. By design it collects all checked child values and submits them as one comma-joined string via ElementInternals.setFormValue(values.join(", ")), adopting the last-changed child's name. Meanwhile Drupal's checkboxes element expects separate array entries (checkboxes[Parent]=Parent, …). Core's Checkboxes::valueCallback() turns the CSV into a single bogus array key, and FormValidator rejects it. The child checkboxes stay silent when grouped (groupExist === true), so the group is the sole submitter.

Possible approaches:

  1. Template-only: remove the wrapper so each submits its own array entry and manually re-create the checkboxgroup fieldset manually with nys-label and nys-errormessage included explicitly
  2. JS / editing the group: behavior is in the compiled upstream package; value: null is overridden at runtime. Need to request a special prop from NYSDS team.
  3. PHP #value_callback override in a module.

Issue fork nys_ds-3593900

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

loopy1492 created an issue. See original summary.

loopy1492’s picture

Issue summary: View changes
loopy1492’s picture

Issue summary: View changes
loopy1492’s picture

Possible solution:

diff --git a/node_modules/@nysds/nys-checkbox/dist/nys-checkbox.js b/node_modules/@nysds/nys-checkbox/dist/nys-checkbox.js
index 526e76f..9ecb735 100644
--- a/node_modules/@nysds/nys-checkbox/dist/nys-checkbox.js
+++ b/node_modules/@nysds/nys-checkbox/dist/nys-checkbox.js
@@ -31,7 +31,17 @@ const y = class y extends _ {
     super.disconnectedCallback(), this.removeEventListener("nys-change", this._handleCheckboxChange), this.removeEventListener("nys-other-input", this._handleOtherInput), this.removeEventListener("invalid", this._handleInvalid), this.removeEventListener("nys-error", this._handleChildError), this.removeEventListener("nys-error-clear", this._handleChildErrorClear);
   }
   firstUpdated() {
-    this._setGroupExist(), this._updateCheckboxSize(), this._updateCheckboxTile(), this._updateCheckboxShowError(), this._getSlotDescriptionForAria();
+    this._setGroupExist(), this._updateCheckboxSize(), this._updateCheckboxTile(), this._updateCheckboxShowError(), this._getSlotDescriptionForAria(), this._initFormValue();
+  }
+  // Seed the submitted value from any pre-checked children on first render.
+  // Server frameworks (Drupal, Rails, PHP) re-render the form with previously
+  // checked boxes after a validation error on another field; without this the
+  // group only sets its value on user interaction, so an untouched-but-checked
+  // group would submit nothing on the next attempt.
+  _initFormValue() {
+    this._internals.setFormValue(
+      this._buildFormValue(Array.from(this.querySelectorAll("nys-checkbox")))
+    );
   }
   updated(e) {
     e.has("required") && this.required && this._setupCheckboxRequired(), e.has("size") && this._updateCheckboxSize(), e.has("tile") && this._updateCheckboxTile(), e.has("inverted") && this._updateCheckboxInvert(), e.has("showError") && this._updateCheckboxShowError(), e.has("form") && this._updateCheckboxForm();
@@ -159,7 +169,19 @@ const y = class y extends _ {
     const t = e, { name: r } = t.detail, s = Array.from(
       this.querySelectorAll("nys-checkbox")
     ), l = s.filter((c) => c.checked).map((c) => c.value);
-    this.name = r, this._internals.setFormValue(l.join(", ")), this._checkOtherInputs(s), this._hasOtherError || this._manageRequire();
+    this.name = r, this._internals.setFormValue(this._buildFormValue(s)), this._checkOtherInputs(s), this._hasOtherError || this._manageRequire();
+  }
+  // Submit each checked child as its own form entry so server frameworks
+  // (Drupal, Rails, PHP) that expect `name[key]=value` parse multiple
+  // selections correctly, instead of one comma-joined string. Falls back to
+  // the group name when children carry no name of their own.
+  _buildFormValue(all) {
+    const checked = all.filter((c) => c.checked);
+    if (!checked.length) return null;
+    const fd = new FormData();
+    for (const c of checked)
+      fd.append(c.getAttribute("name") || this.name, c.value);
+    return fd;
   }
   async _handleChildError(e) {
     e.stopPropagation();
@@ -171,10 +193,9 @@ const y = class y extends _ {
     this._otherErrorCheckbox && r !== this._otherErrorCheckbox || (this._hasOtherError = !1, this._otherErrorCheckbox = null, this._internals.setValidity({}), this.showError = !1, this.required && !this._hasAtLeastOneChecked() && this._manageRequire());
   }
   _handleOtherInput() {
-    const t = Array.from(
-      this.querySelectorAll("nys-checkbox")
-    ).filter((r) => r.checked).map((r) => r.value);
-    this._internals.setFormValue(t.join(", "));
+    this._internals.setFormValue(
+      this._buildFormValue(Array.from(this.querySelectorAll("nys-checkbox")))
+    );
   }
   async _checkOtherInputs(e) {
     for (const t of e)
loopy1492’s picture

Adding the value: element['#value'], to the component seems to help it pass validation with Drupal, but when the form returns back with edits, the javascript seems upset that a value has been set.

loopy1492’s picture

The NYSDS adds the "name" of the selected checkbox to the fieldset every time it is clicked. In the NYSDS example, it is expected that a checkbox group will all have the same "name". In Drupal, this is not the case. It does appear that the NYSDS uses the proper method. We will have to try using the fieldset name in the indivudal checkboxes. Hopefully that does not cause data saving issues.

loopy1492’s picture

The merge request includes code to "patch" the existing checkboxgroup component in lit.

loopy1492’s picture

Status: Active » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

loopy1492’s picture

Status: Fixed » Closed (fixed)