Gracefully handle components in active development: ensure great DX
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3532514. -->
Reported by: [heyyo](https://www.drupal.org/user/474388)
Related to !196 !153 !223 !219 !205 !1299
>>>
<h3 id="overview">Overview</h3>
<p><em>Follow-up for <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3470422" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3470422</a></span>.</em></p>
<pre> /**<br> * Whether the logic powering this component is broken.<br> *<br> * Typical example: a developer is developing an SDC, and while developing is<br> * testing it in Canvas. They're even renaming the SDC. It'd be a terrible DX<br> * if this caused the associated Component config entity to switch to the<br> * fallback version.<br> *<br> * @see \Drupal\canvas\Entity\Component::getComponentSourcePluginId()<br> * @return bool<br> */<br> public function isBroken(): bool;</pre><p>… but there's more we could detect!</p>
<p>Quoting <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3470422" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3470422</a></span>:</p>
<blockquote><h3>Out of scope: handle more breakage scenarios when developing components</h3>
<p>There's <em>more</em> things that can <u>fundamentally</u> break a component instance though:</p>
<ol>
<li>✅ (<del>this</del> <em>that</em> issue) the underlying component disappeared from the <code>ComponentSource</code> (whether deleted or renamed)
</li><li>❌ the set of <em>required</em> explicit inputs increased (SDC terminology: "props") — <em>decreasing is not a problem!</em>
</li><li>❌ the set of <em>required</em> explicit inputs changed (SDC terminology: "props") aka a required explicit input was renamed
</li><li>❌ the shape of an explicit input (SDC terminology: "props") changed, e.g. a <code>type: string</code> becoming a <code>type: integer</code>
</li></ol>
<p>⚠️ Currently, there's also ways to break things in Canvas' UI that <u>are not fundamental</u>, such as passing an optional prop that either no longer exists or has been renamed. (That currently triggers the validation in <code>\Drupal\canvas\Plugin\Canvas\ComponentSource\GeneratedFieldExplicitInputUxComponentSourceBase::getDefaultStaticPropSource()</code> but arguably should not. See @heyyo's <span class="drupalorg-gitlab-issue-link project-issue-status-info project-issue-status-7"><a href="https://www.drupal.org/project/canvas/issues/3532514" title="Status: Closed (fixed)">#3532514: Gracefully handle components in active development: ensure great DX</a></span>.)</p>
<p>(This may cause garbage values though, for that we have <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3524401" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3524401</a></span>.)
</p></blockquote>
<p>This issue is about:</p>
<ul>
<li>fixing the 1 ⚠️ , which is what most comments on this issue have been about so far, see especially @heyyo's #18</li>
<li>fixing the 3 ❌, the first 2 correspond to what @catch wrote in #6, the 3rd was not yet discussed here</li>
</ul>
<h3 id="proposed-resolution">Proposed resolution</h3>
<ol>
<li>✅ Track which explicit inputs <em>are required</em> in the versioned information in a <code>Component</code> config entity for:
<ul>
<li>✅ <code>GeneratedFieldExplicitInputUxComponentSourceBase</code>-based implementations (SDC + code components) →<br>
<pre>diff --git a/config/schema/canvas.schema.yml b/config/schema/canvas.schema.yml<br>index 7c7c57ee6..e5aa02441 100644<br>--- a/config/schema/canvas.schema.yml<br>+++ b/config/schema/canvas.schema.yml<br>@@ -459,6 +459,9 @@ canvas.generated_field_explicit_input_ux:<br> sequence:<br> type: mapping<br> mapping:<br>+ required:<br>+ type: boolean<br>+ label: 'Required prop'<br> field_type:<br> type: string<br> label: 'Default field type'</pre><p>
→ <a href="https://git.drupalcode.org/project/canvas/-/merge_requests/205">MR 205</a> </p>
</li><li>✅ … which will require an update path → <a href="https://git.drupalcode.org/project/canvas/-/merge_requests/219">MR 219</a>
</li><li>ℹ️ This also enables important future work: [3463996] and <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3509115" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3509115</a></span>.
</li></ul>
</li><li>✅ Update <code>GeneratedFieldExplicitInputUxComponentSourceBase</code> to stop relying on the live code implementation (the underlying SDC or code component) whenever possible and rely on the <code>prop_field_definitions</code> in the <code>Component</code> config entity instead:
<ol>
<li>✅ <code>::buildComponentInstanceForm()</code> should stop calling <code>::getDefaultStaticPropSource(…, validate_prop_name: TRUE)</code> — pass <code>FALSE</code> instead. Consider only calling that if A) assertions are on (indicating a developer) and B) Twig developer mode is off (because it'd otherwise get in the way <em>while</em> developing the SDC!)<br>
→ended up happening in <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3470422" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3470422</a></span>
</li><li>✅ Update <code>::buildComponentInstanceForm()</code> and <code>::getDefaultStaticPropSource()</code> to maximally rely on <code>prop_field_definitions</code> in the <code>Component</code> — which also means we're finally respecting the prop order defined in the SDC/code component! 🥳<br>
→ <a href="https://git.drupalcode.org/project/canvas/-/merge_requests/196">MR 196</a>
</li></ol>
</li>
<li>✅ Ensure that even an exception while constructing the render array for a component instance in <code>GeneratedFieldExplicitInputUxComponentSourceBase::renderComponent()</code> cannot trigger a PHP fatal error: wrap that also in the <code>RenderSafeComponentContainer</code>, specifically in <code>Component::normalizeForClientSide()</code>, this ensures that the list of available components to instantiate correctly at runtime reflects what is broken and not → <a href="https://git.drupalcode.org/project/canvas/-/merge_requests/196">MR 196</a>
</li><li>🚫 <del>Consider renaming: <code>::isValid()</code>?</del> → not stable-blocking, defer that to the overall goal of making the component source plugin API public & stable in <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3520484" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3520484</a></span>.
</li></ol>
<h3 id="ui-changes">User interface changes</h3>
<p>Rather than just "Component is broken" in<br>
<img src="https://www.drupal.org/files/issues/2025-09-26/Captura%20de%20pantalla%202025-09-26%20a%20las%2013.52.36.png">, when "verbose error logging" is enabled, you'd instead see something much more detailed/helpful, both inside Canvas and on the live site:<br>
<img src="https://www.drupal.org/files/issues/2025-10-09/Screenshot%202025-10-09%20at%2023.01.51.png" alt="Screenshot"><br>
<img src="https://www.drupal.org/files/issues/2025-10-09/Screenshot%202025-10-09%20at%2023.02.10.png" alt="Screenshot"></p>
> Related issue: [Issue #3536277](https://www.drupal.org/node/3536277)
> Related issue: [Issue #3523841](https://www.drupal.org/node/3523841)
> Related issue: [Issue #3521221](https://www.drupal.org/node/3521221)
issue