diff --git a/src/ClientDataToEntityConverter.php b/src/ClientDataToEntityConverter.php index cf2e99650d2c8356b93adf937cff4a50f84bd80c..18e16bb10785099226d3e9723824e09debe9a601 100644 --- a/src/ClientDataToEntityConverter.php +++ b/src/ClientDataToEntityConverter.php @@ -170,7 +170,14 @@ class ClientDataToEntityConverter { } } - $entity_form_fields = \array_filter($entity_form_fields, static fn (array|string $value, string|int $key): bool => !\in_array($key, $boolean_fields, TRUE) || $value !== ['value' => '0'], ARRAY_FILTER_USE_BOTH); + $entity_form_fields = \array_combine(\array_keys($entity_form_fields), \array_map(static fn (string|int $key): array|string => + // Unchecked boolean checkboxes are expected to be set with value NULL. For a normal + // form submission, this is done for us by the Form Builder. But for a + // programmatic form submission, this needs to be done manually. + // @see \Drupal\Core\Form\FormBuilder::handleInputElement + (!\in_array($key, $boolean_fields, TRUE) || $entity_form_fields[$key] !== ['value' => '0']) ? $entity_form_fields[$key] : ['value' => NULL], + \array_keys($entity_form_fields), + )); $form_object = $this->entityTypeManager->getFormObject($entity->getEntityTypeId(), 'default'); $form_object->setEntity($entity); // Flag this as a programmatic build of the entity form - but do not flag diff --git a/tests/modules/xb_test_article_fields/xb_test_article_fields.install b/tests/modules/xb_test_article_fields/xb_test_article_fields.install index 2e3d315966739d8b77bc35faad4d5a8044dd516d..019662e723c95832344c54972160626c94c7b796 100644 --- a/tests/modules/xb_test_article_fields/xb_test_article_fields.install +++ b/tests/modules/xb_test_article_fields/xb_test_article_fields.install @@ -328,6 +328,36 @@ function xb_test_article_fields_install(): void { ], ], ], + 'field_xbt_boolean_checkbox' => [ + 'type' => 'boolean', + 'label' => 'XB Boolean Checkbox (default true)', + 'settings' => [ + 'on_label' => 'Yes', + 'off_label' => 'No', + ], + 'widget' => [ + 'type' => 'boolean_checkbox', + 'settings' => [], + ], + 'default_value' => [ + ['value' => TRUE], + ], + ], + 'field_xbt_boolean_checkbox2' => [ + 'type' => 'boolean', + 'label' => 'XB Boolean Checkbox (default false)', + 'settings' => [ + 'on_label' => 'Yes', + 'off_label' => 'No', + ], + 'widget' => [ + 'type' => 'boolean_checkbox', + 'settings' => [], + ], + 'default_value' => [ + ['value' => FALSE], + ], + ], ]; // Setup content moderation for article node type. diff --git a/ui/tests/e2e/entity-form-fields/field_xbt_boolean_checkbox.js b/ui/tests/e2e/entity-form-fields/field_xbt_boolean_checkbox.js new file mode 100644 index 0000000000000000000000000000000000000000..6ac2ecd09ed93098b2e05bf4540645f5043d425e --- /dev/null +++ b/ui/tests/e2e/entity-form-fields/field_xbt_boolean_checkbox.js @@ -0,0 +1,25 @@ +export const edit = (cy) => { + cy.findByLabelText('XB Boolean Checkbox (default true)').as('checkbox'); + cy.get('@checkbox').should('have.attr', 'aria-checked', 'true'); + cy.get('@checkbox').click(); + cy.get('@checkbox').should('have.attr', 'aria-checked', 'false'); + // Wait for the preview to finish loading. + cy.wait('@updatePreview'); + cy.findByLabelText('Loading Preview').should('not.exist'); + + // Trigger a new intercept for the main test to wait for. + cy.intercept({ + url: '**/xb/api/layout/node/2', + times: 1, + method: 'POST', + }).as('updatePreview'); + cy.findByLabelText('XB Boolean Checkbox (default false)').as('checkbox'); + cy.get('@checkbox').should('have.attr', 'aria-checked', 'false'); + cy.get('@checkbox').click(); + cy.get('@checkbox').should('have.attr', 'aria-checked', 'true'); +}; + +export const assertData = (response) => { + expect(response.attributes.field_xbt_boolean_checkbox).to.eq(false); + expect(response.attributes.field_xbt_boolean_checkbox2).to.eq(true); +}; diff --git a/ui/tests/e2e/entity-form-fields/index.js b/ui/tests/e2e/entity-form-fields/index.js index 7af9a0726311e37c8eef8b69adba49b0382b9939..69179123b3ce134df02cbd2ce542fe3960a7743b 100644 --- a/ui/tests/e2e/entity-form-fields/index.js +++ b/ui/tests/e2e/entity-form-fields/index.js @@ -13,6 +13,7 @@ import * as field_xbt_datetime_timestamp from './field_xbt_datetime_timestamp.js import * as field_xbt_daterange_datelist from './field_xbt_daterange_datelist.js'; import * as field_xbt_datetime_datelist from './field_xbt_datetime_datelist.js'; import * as field_xbt_entity_ref_tags from './field_xbt_entity_ref_tags.js'; +import * as field_xbt_boolean_checkbox from './field_xbt_boolean_checkbox.js'; // Expand this to add additional coverage. // For each field to be tested, add a new file that exports two methods as @@ -41,4 +42,5 @@ export default { field_xbt_daterange_datelist, field_xbt_datetime_datelist, field_xbt_entity_ref_tags, + field_xbt_boolean_checkbox, };