Overview
Lets begin with the non-theoretical: A symptom of this issue can be seen at #3535986: Autocomplete machine name handling does not match to machine name, webform picking does not work, where an entity autocomplete field in a block is not storing the expected value because the element validate callback isn't being called.
Block plugins that meet the requirements of \Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\BlockComponent::checkRequirements will show up as Dynamic Components that can be added to a layout.
Like SDCs and code components, these have their own component instance form, where the block settings can be edited. The component settings form for Blocks works in the same manner: Values in the form correspond to values in the components model. As the values change, the model in the Redux store is updated. Meeting he criteria of checkRequirements is what ensures a Block Plugin can be edited with component instance form
Much of the time, editing these settings via the component instance form works fine, and we have the tests to prove it. Unfortunately, this does not account for all uses cases because validate and submit handlers are not called at any point in the component instance authoring/publishing lifecycle. For example, if a block config setting uses the 'entity_autocomplete', it assumes the validateEntityAutocomplete callback will be invoked - a callback that converts reference labels to the ids needed for it to work properly and pass validation.
For SDC/CodeComponent editing this isn't an issue - this is functionality introduced with XB that has a dataflow that is indifferent to #validate #submit, etc. Block plugin settings, on the other hand, are something that have existed for quite some time, and with their own validation/submit/etc assuming forms. There likely needs to be something that bridges the Drupal they were built for and the XB that is using them.
Proposed resolution
One option to mention, though it's probably not a good one: add a bunch of new transforms to account for block settings that need this extra treatment, and this would likely mean expecting contrib to provide them as well. I suspect this could get unwieldy as this would mean transforms for specific fields in specific blocks - a far more demanding task than the per-prop-type transforms we currently maintain. It would also introduce a burden on contrib developers to support transforms that replicate logic they've already written in their block plugin.
Another option that might be more scalable begins with acknowledging that:
- Block settings forms already exist (
admin/structure/block/manage/foo) - XB already has a page data tab that invokes the validation/submit logic of an entity form
It seems worth looking into making the Block Component form work a little more like the Page Data form, which invokes the same validate+submit callbacks that it would as a non-XB entity form - albeit at different stages in the authoring process. This might not be a thing we can just do, as the Page Data form has its own Redux Slice real estate, while block settings are currently expected to tidily fit into the prop model structure.
User interface changes
| Comment | File | Size | Author |
|---|---|---|---|
| #42 | Screenshot 2025-08-19 at 1.37.04 PM.png | 189.52 KB | bnjmnm |
| #40 | Screen Recording 2025-08-19 at 7.01.54 PM.mov | 35.03 MB | mayur-sose |
| #38 | Screen Recording 2025-08-19 at 3.14.06 PM.mov | 9.88 MB | mayur-sose |
| #34 | boolean block settings.gif | 125.28 KB | wim leers |
| #30 | Screenshot From 2025-08-13 14-28-31.png | 427.52 KB | larowlan |
Issue fork experience_builder-3523496
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
Comment #2
wim leersThis AFAIK ties into follow-ups opened for #3475584: Add support for Blocks as Components that were never followed up on. A quick scan suggests:
\Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\BlockComponent::fixBooleansUsingConfigSchema())Comment #3
wim leersThis should be fixed before 1.0, but not necessarily before #3515932: Milestone 1.0.0-beta1: Enable creation of non-throwaway sites — because A)
ComponentSourceplugins won't be a stable API yet, B) I don't see how this could impact the data storage.Comment #4
wim leersIf we're lucky, it's this simple:
Comment #6
gábor hojtsyI'm running into this in #3535986: Autocomplete machine name handling does not match to machine name, webform picking does not work with the webform block.
Webform uses
Drupal\Core\Entity\Element\EntityAutocompletewhich has avalidateEntityAutocomplete()which is where the mapping happens. That is defined as an#element_validateon the element. I tried the proposed fix in #4, that did not seem to help (change anything at all with what it did).Comment #7
bnjmnmThe
validateConfigurationForm/submitConfigurationFormmethods inExperienceBuilder/ComponentSource/BlockComponentaren't called at all, so the additions in #4 unfortunately won't work. This isn't a huge shock as (like the Page Data form) there isn't a traditional submit that would invoke the validate/submit methods in the way we're accustomed to.I attempted to submit the block form similar to how the entity form is programmatically validated/submitted in
\Drupal\experience_builder\ClientDataToEntityConverter::setEntityFieldsand didn't have any luck - if elements in the form had#element_validatecallbacks they weren't called. I suspect this is because of how complex it is to do this (a delicate balance of form state and form and input values) and not because it's an unworkable approach.Comment #8
wim leers@bnjmnm Ah, right, this also needs
ComponentInputsForm::validateForm()andComponentInputsForm::submitForm()to callComponentSourceInterface::(validate|submit)Form— that's what the issue title was literally reporting, forgot to call that out in my diff above 😅Comment #9
effulgentsia commentedComment #10
f.mazeikis commentedCurrently, as per @bnjmnm comment #7, we do not call methods proposed by @wim-leers in #4. I've tried adding suggested code as per #7, but that isn't called either.
In fact,
ComponentInputsForm::buildForm()has the following comments on L102-L106:I do not think we should classify this as a bug, as we've never built form submission/validation handling mechanism. We build form to allow user to input prop values, but the only validation performed is on FE side. At least until an attempt to publish/save is made, at which point some form inputs turn out to be invalid, as they were never massaged by "submission" mechanisms.
I suspect that this will be much more involving than just adding code proposed in #4 and #7. Specifically, adapting FE application to do submissions and validation via Drupal methods.
Considering that current UI allows user to enter some text into autocomplete, select a value and click away - there might be some interesting challenges to figure out what constitutes "form submission" or "form requiring validation" if there are no additional UI/UX changes introduced along with this.
And even when such changes are in, I suspect we will need thorough manual and automated testing to find potential edge cases.
Comment #11
bnjmnmComment #12
larowlanI have some thoughts, I'll push this along a bit
Comment #13
larowlanComment #15
larowlanPushed up a POC but we've got a couple of open threads/questions
\Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\BlockComponent::buildConfigurationFormwe're only calling::blockFormand we probably should be calling::buildConfigurationForm- current code is not calling the code for blocks that override that method - e.g.\Drupal\layout_builder\Plugin\Block\InlineBlock::buildConfigurationForm#tree => TRUEon the form. If we do don't do that we end up with a mismatch in how the form works between here and normal block forms. But if we do that, we end up with extra values in the input names - e.g. see\Drupal\system\Plugin\Block\SystemBrandingBlock::blockSubmitwhich expects to seeblock_brandingin the values. We don't currently have that because we're working with the schema for the config (as seen in\Drupal\system\Plugin\Block\SystemBrandingBlock::defaultConfigurationwhere there is noblock_branding. We also have\Drupal\experience_builder\Form\ComponentInputsForm::applyElementParentsto mess with#parents.I suspect the way forward will be to get
\Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\BlockComponent::buildConfigurationForminto alignment withBlockFormand use sub-form state and properly pass alongsettingsand#tree => TRUEto the FE, then in::clientModelToInputwe can undo that in a repeatable fashion, so that we can call::blockSubmitfor blocks likeSystemBrandingBlockThe failing e2e catches these issues 🙌 so that block was a good choice for test cases!
Comment #18
larowlan@gábor hojtsy any chance you could test this branch with webform to confirm it resolves the issue?
The test module I've added has a block form with an entity autocomplete widget so 🤞 it does.
Famous last words 😂, this one turned out to be quite the battle
Comment #19
gábor hojtsyTried the latest MR in xb-demo. I unapplied the change to the block to make it a select box, so it is back to needing this fix, then applied this fix. Unfortunately when I try to enter something in the webform autocomplete that is not quite correct (eg. I start typing to wait for autocomplete to return), I get a red error in place of the form saying it did not render. I tried looking in the console logs but no useful data there. I tried reloading but it got into an "undo last action" loop.
The logs have these:
followed by
Line 259 in WebformBlock is the middle of this:
so if this gets NULL, that does not fulfill what the code expects assuming the default config for the block which is:
Is this considered in the code/XB?
Comment #20
wim leersYes, it is:
—
\Drupal\experience_builder\Plugin\BlockManager::setCachedDefinitions()Will review & investigate #19.
Comment #21
lauriiiWe need to test #3513742: Machine name JS does not work in Page Data/Component Instance form after this because per @bnjmnm, this issue should address that too 👏
Comment #22
bnjmnmTo clarify: the issue as reported (which has to do with the JS logic in machine-name.js) is not something that would be fixed here, but it likely already works due to other recent changes.
There was an unrelated bug mentioned in #3513742 that is what #21 is referencing. That bug is related to problems with webform block autocomplete values that expect a machine name. It's also is what @gábor hojtsy tested in #19 - so it's already part of this evaluation - not something additional.
Comment #23
wim leers@bnjmnm's issue summary update in #11 is superb — thanks, Ben! 🤩
#3537872: Redux-aware form input components should be aware of direct element manipulation landed yesterday. @Gábor Hojtsy, could you please re-test #19 — I've merged upstream changes into this MR 👍
@larowlan: AFAICT this would make #3509265: Auto-saving of blocks needs to handle string-to-bool fixing obsolete — do you concur?
#15:
I disagree.But per what's below, I was forced to agree nonetheless 😅#3484666: Show configuration forms for Block components added the call to
::blockForm(). It did that because it is the only method actually specified on the block plugin:\Drupal\Core\Block\BlockPluginInterface::blockForm().::buildConfigurationForm()is not guaranteed to be implemented, and is specifically tailored towardsBlockconfig entities, because:\Drupal\Core\Block\BlockPluginTrait::buildConfigurationForm()is what implements it — an optional (but encouraged) trait, also used by\Drupal\Core\Block\BlockBaseblock-sourced components, 100% of which is irrelevant when used in XB; it's only relevant forBlockconfig entities:👆 Of those:
provideris pure noisewhich has a core issue to refactor it away— nope I mixed that up with #3426278: Deprecate unused `provider` exported property from Block config entitiesadmin_labelis used only at/admin/structure/block, to allow site builders to relabel placed block [config entities] to make sense to them, in that particular UIlabel(and its siblinglabel_display) is used byblock.html.twig, which XB doesn't use, because that's associated with Block config entities😬🫠 … except that apparently
type: block_settingsdictates thatlabel,label_displayetc. must all be present always 🫠… which means this is the right thing to do, and this should pave the path for fixing #3526118: CSS assuming block-plugin-rendered-in-a-wrapper-for-the-block-config-entity does not apply to XB: either A) stay the course, B) simulate the same markup structure, too.
Plus, you've sidestepped the UX nightmare I identified by working your magic in
BlockComponent::buildAnonymousFormForBlockPlugin()to remove those 4 form fields 🧙🏽♂️👍Comment #24
bnjmnmMight sound like splitting hairs, but IMO an important distinction to figuring this out:
What @gábor hojtsy is encountering in #19 is something introduced by the MR. In other words it's not part of the underlying problem that this MR failed to account for.
We're now invoking the submit/validate callbacks on the block form. That's overall a good thing, it's needed to massage values to the expected end result.
BUT... The problem is due to logic in
\Drupal\webform\Plugin\Block\WebformBlock::blockSubmit, which is now invoked as early as adding the Webform Block to the layout.It's looking for
$values['webform_id'], which isNULL, instead of the default value which is an empty string.NULL will fail the
!is_null()assertion in\Drupal\Core\Entity\EntityStorageBase::load, but an empty string is handled fine.Comment #25
wim leersWhoops, I read over this bit in @Gábor Hojtsy's comment:
@bnjmnm is right in #24: that is expected: invalid component inputs SHOULD result in a failure to render, but not in a way that it's a hard crash — see the screenshots in #3485878: Server-rendered component instances should NEVER result in a user-facing error, should fall back to a meaningful error instead (+ log). That must be what Gábor is seeing.@Gábor: why do you expect that an invalid/nonsensical value would result in automatically all user input getting ignored and instead the block falling back to the default?Nope, that's the block component instance form that failed to render, not the corresponding block component preview. Which means I've got nothing to add to Ben's comment :D But if the webform block submission logic wouldn't crash on this, then you'd see what I just described above (and struck through).
🙏 Gábor, next time please post screenshots, that makes it much easier to understand what you're referring to 😇
Comment #26
wim leersThis MR looks equals parts impressive and scary — in #23 I articulated my main concern, but then was forced to withdraw it :D (aka concluding I was wrong!)
I did spot a number of other concerning things though — concerning because I'm struggling to grok them 😅
The most important ones:
resolvedbut that then not matching the exact inputs expected byblock-sourced components: https://git.drupalcode.org/project/experience_builder/-/merge_requests/1...BlockComponentandClientDataToEntityConvertercan use — suggestion at https://git.drupalcode.org/project/experience_builder/-/merge_requests/1... — without this, I worry about maintainability, so IMHO it should happen in this MR.::fixBooleansUsingConfigSchema()still being around despite your remark at https://git.drupalcode.org/project/experience_builder/-/merge_requests/1... — see— https://git.drupalcode.org/project/experience_builder/-/jobs/6186837
(It fails consistently.)
Comment #27
wim leersAlso, AFAICT this:
Comment #28
bnjmnmI made a small change that (at least in my manual testing) addresses the Webform Block problem mentioned in #19. 🤞 this doesn't result in a bunch of exploding tests, but easy enough to revert if it does.
Comment #29
larowlanI reverted #28 because it caused a regression in the changes here (the test for the new behaviour failed).
I will see if I can work out a different way to do it.
Comment #30
larowlanSo the net cause of #19 is that in a traditional form the #required = TRUE would prevent us getting to the submission handler, but we don't want to prevent that in XB
Made some changes and it seems to be working now
Comment #31
wim leersYay, #3484669: Define how block settings should be presented in the UI is now indeed closed, but I was wrong about #3500795: [PP-1] Implement client-side validation of block settings (see #16 and onwards there): while this issue's MR adds validation, it doesn't add client-side validation, and that'd still be a boon for UX. See the discussion there for concrete proposals for how to achieve that.
The adoption of
#treebrokeBlockComponent::fixBooleansUsingConfigSchema()— see https://git.drupalcode.org/project/experience_builder/-/merge_requests/1...Comment #32
wim leersPlacing the admin menu block and configuring it to start at level 1 and show 3 levels works and renders and is valid and saves, but … I see this getting stored in the
inputsfield property for my content-defined component tree:🐛 Strings, not integers!
And if I create a
Patternout of it (to create a config-defined component tree):🐛 Again strings, not integers!
🤯 I'd expected config schema validation to catch this, but while
BlockComponent::validateComponentInput()does call config schema validation, it considers these valid! (Because it does the necessary casting based on the schema.)Yet if I place the exact same block plugin as a block and use the exact same form to set the same settings, I get the following
Blockconfig entity:👆 No strings, but integers.
Will investigate…
Comment #33
wim leersApparently #32 already is the case in HEAD. Created new issue for it: #3541125: Integer BlockComponent inputs (i.e. block settings) are incorrectly saved as strings.
Comment #34
wim leersSo:
$client_model = ['resolved' => …]to hopefully save the next person time, and retitled (already existing issue) #3521041: [META] Refactor the organically grown Component metadata mess in PHP (`::getClientSideInfo()`) and JS (`BaseComponent`, `DynamicComponent`, `PropSourceComponent` etc.) to convey this is a system (DX) problem in the XB codebase that will continue to slow us down::fixBooleansUsingConfigSchema()because it was clearly dead code — which makes my #31 obsoleteBlockComponent::(validate|submit)ConfigurationFormare still untouched/unused even after making all this work (they were introduced as no-ops in #3484666: Show configuration forms for Block components) just proves that we won't need these, because the component instance form will NEVER be a traditional server-side form, so: removedGiven this is so clearly such an important leap forward, even if there may be imperfections, I am trying to meet with @bnjmnm in the next 30 mins, otherwise I'm going ahead and merging 😇
Just see the lovely results:
@bnjmnm, feel free to reopen this issue if you have lingering concerns, we can do follow-up MRs 👍
Comment #35
wim leersWalked @bnjmnm through my findings and changes, and he then double-checked the comments I added. He's +1 so … yay, let's get this in! 🥳
Comment #37
wim leersThanks, all, and especially @larowlan & @bnjmnm!
Comment #38
mayur-sose commentedI enabled the Test Block Components module to verify this issue. While testing, I encountered an error—please see the attached video for details.
Console error:
{ "status": 500, "data": { "message": "Drupal\\experience_builder\\Plugin\\ExperienceBuilder\\ComponentSource\\BlockComponent::clientModelToInput(): Argument #3 ($client_model) must be of type array, null given, called in /var/www/html/web/modules/contrib/experience_builder/src/Form/ComponentInputsForm.php on line 102", "file": "/var/www/html/web/modules/contrib/experience_builder/src/Plugin/ExperienceBuilder/ComponentSource/BlockComponent.php", "line": 403, "trace": [ { "file": "/var/www/html/web/modules/contrib/experience_builder/src/Form/ComponentInputsForm.php", "line": 102, "function": "clientModelToInput", "class": "Drupal\\experience_builder\\Plugin\\ExperienceBuilder\\ComponentSource\\BlockComponent", "type": "->" }, ] } }@wim-leers, could you please provide the testing steps required to verify this issue?
Comment #39
wim leers@mayur-sose Test using the https://drupal.org/project/webform contrib module — before this MR that should fail, with this MR it should work — see the screenshot in #30.
Comment #40
mayur-sose commentedVerified the changes :
Feedback or error messages are shown if invalid.
Note: Please see the attached video ("TC3-invalid-entity-autocomplete.mp4") for the reproduction of this failure.
@wim-leers, please check attached video for TC3, failed test case.
Comment #41
wim leersNice find, @mayur-sose!
AFAIK, "undo last action" should've in principle worked: it should've undone the change from
Contact (contact)(i.e. successfully autocompleted and executed#element_validate => EntityAutoComplete::validateEntityAutocomplete) totest(which was not a valid autocomplete result).But it didn't.
This gets too far into FE territory for me to determine appropriate next steps, but I'm pretty sure @bnjmn knows 🤓😊 So handing this over to him! See also his excellent overview comment at #3513742-12: Machine name JS does not work in Page Data/Component Instance form. I suspect he'll want to repurpose #3513742 to tackle this remaining bit?
P.S.: closed #3535986 at #3535986-4: Autocomplete machine name handling does not match to machine name, webform picking does not work.
Comment #42
bnjmnm#40 TC doesn't seem particularly Front End to me - it looks like we're allowing invalid values to be published, which can result in errors when attempting to render.
Based on the description / video:
From the looks of it, the block is being published despite not meeting validation criteria. When we attempt to do this in OG Drupal, it's not possible to submit:
I suspect we need to do something like #3521759: Publish checks validation constraints, but not form validation but targeted to blocks, so content with invalid field values aren't publishable.
Created #3542102: Prevent publishing when `block` component instance values do not #validate for what was spotted in #40
Created #3542101: Provide better info than "an unexpected error has occurred", to increase bug report quality because it's unfortunate how much helpful info is literally just sitting there to make our lives easier, but we only get "an unexpected error has occurred"
Comment #44
wim leersLooks like we missed an edge case: #3561919: Block plugins with checkboxes that are not in the top level of the form do not get the default values assigned.