chore(Data model): #3591727 Store each prop's translatability per component version so deleting an optional prop keeps config-defined component trees valid
Closes #3591727
Summary
ComponentInputsMapping (introduced in #3582478 (closed)) validates every config-defined component tree's inputs, but it builds each component instance's input schema from the component's live JSON Schema (getMetadata()), not the version the instance actually references. So deleting an optional prop from an SDC or code component — which creates a new component version — made previously valid-and-unchanged config-defined trees (still pointing at the old version) report the removed input as '<prop>' is not a supported key. This is a regression for monolingual sites and a bug for multilingual sites.
Root cause
ComponentInputs::resolveConfigSchemaMapping() → JsonSchemaPropsComponentInstanceInputsConfigSchemaGenerator::getConfigSchemaMapping() → JsonSchemaPropsComponentSourceBase::getExplicitInputDefinitions() → getMetadata(), which can only return the live/deployed schema. The mapping therefore omits props that the referenced version had but the live implementation no longer does.
Approach
Store each prop's translatability in the component version itself:
- Add an optional
translatableboolean toprop_field_definitions(canvas.json_schema_props), computed at discovery. The JSON Schema itself cannot be stored: an array prop'sitems.meta:enumkeys may contain dots, which config keys cannot hold. - It is stripped from the component version hash (
ComponentSourceBase::generateVersionHash()), so no existing version ids change — it is fully derived from data already hashed (the explicit-input schema and the prop's field type expression). - The schema generator now builds the mapping from the versioned prop set, so a removed/changed prop keeps a mapping entry, and uses the stored flag.
- Backwards compatible: versions saved before this lands have no flag and fall back to recomputing translatability from the live shape (and automatic component-instance updating already repairs instances).
- The translatability rule is centralized in
JsonSchemaPropsComponentSourceBase::isExplicitInputTranslatable().
Testing
-
ddev xb-phpunit tests/src/Kernel/ApiAutoSaveControllerTranslationTest.php -
ddev xb-phpunit tests/src/Kernel/Plugin/Canvas/ComponentSource/SingleDirectoryComponentTest.php -
ddev xb-phpunit tests/src/Kernel/Plugin/Canvas/ComponentSource/JsComponentTest.php -
composer phpcs && composer phpstan - Manual: place a config-defined component tree (e.g. a PageRegion) using an SDC/code component with an optional string prop set; remove that prop from the component; confirm the region stays valid (was:
'<prop>' is not a supported key).
AI usage disclosure
Per Drupal.org's policy on AI contributions: root-cause analysis, implementation, and test updates were produced with Claude (Opus 4.8) and reviewed by the human contributor before pushing.