[PP-1] Entity Reference Selection UI
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3585355. -->
Reported by: [penyaskito](https://www.drupal.org/user/959536)
>>>
<h3 id="problem-motivation">Problem/Motivation</h3>
<p> Editors configuring a JS code component need a way to declare which entity fields a prop depends on. The config storage was added in <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3585298" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3585298</a></span> as<br>
<code>dataDependencies.entityFields</code>, and the supporting HTTP APIs are added in <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3585354" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3585354</a></span>. This issue wires both together with an editor-facing React UI.</p>
<h3 id="proposed-resolution">Proposed resolution</h3>
<p> Add a "Content Relationship"-style modal dialog to the code component editor. The editor picks an entity type + bundle, then selects fields via checkboxes; reference<br>
fields are expandable and load their children lazily. Selections persist as an array of expression strings in <code>dataDependencies.entityFields[propName]</code>.</p>
<p> <strong>Existing infrastructure reused (no new auto-save wiring):</strong></p>
<ul>
<li><code>codeEditorSlice</code> at <code>ui/src/features/code-editor/codeEditorSlice.ts</code> — already manages <code>state.codeComponent.dataDependencies</code>.</li>
<li><code>setCodeComponentProperty([property, value])</code> action — sets any <code>codeComponent</code> property and flags <code>needsAutoSave</code>.</li>
<li><code>useAutoSave</code> hook — already watches <code>dataDependencies</code> and dispatches the existing<br>
<pre>PATCH<br> /canvas/api/v0/config/auto-save/js_component/{id}</pre><p> mutation.</p></li>
<li>Props panel at <code>ui/src/features/code-editor/component-data/Props.tsx</code> — the entry point ("Bind to entity fields" action on a prop row).</li>
</ul>
<p> <strong>New components</strong> (in <code>ui/src/features/code-editor/component-data/entity-reference/</code>):</p>
<ul>
<li><code>EntityReferenceSelectionDialog</code> — top-level modal, receives <code>propName</code>, owns draft state.</li>
<li><code>TypeSelector</code> — combobox for entity type + bundle (RTK Query against <code>/content-entity-types</code>).</li>
<li><code>FieldTree</code> — recursive tree with checkboxes; expanding a reference field triggers a lazy fetch with <code>parent=[expression]</code>.</li>
<li><code>FieldRow</code> — leaf row rendering (icon, label, reference-target pill, checkbox).</li>
</ul>
<p> <strong>New RTK Query service:</strong><br>
<code>entityReferenceApi</code> slice in <code>ui/src/services/</code> with <code>getContentEntityTypes()</code> and <code>getFields(entityType, bundle, parent?)</code><br>
endpoints, wired into <code>store.ts</code>. Cache tags scoped by entity type + bundle + parent so lazy-loaded subtrees cache independently. 403 responses (from a user's<br>
permissions changing or a parent chain becoming inaccessible) surface as RTK Query errors — consuming components render an error state rather than crashing.</p>
<p> <strong>Type extension:</strong><br>
Extend <code>DataDependencies</code> at <code>ui/src/types/CodeComponent.ts</code> with <code>entityFields?: Record<string, string[]></code>.</p>
<p> <strong>Persistence flow (no direct PATCH from the UI):</strong></p>
<ol>
<li>Dialog opens with <code>propName</code> context; reads current selections from Redux.</li>
<li>Local draft state tracks the working set separately from the store — only commits on Save.</li>
<li>On Save, dispatches<br>
<pre>setCodeComponentProperty(['dataDependencies', { ...current, entityFields: { ...current.entityFields, [propName]: [...selected] }<br> }])</pre><p>. Empty arrays/keys are pruned to respect backend <code>NotBlank</code> semantics.</p></li>
<li>The existing <code>useAutoSave</code> hook observes the store change and auto-saves via the existing mutation.</li>
<li>Cancel discards draft state without dispatching.</li>
</ol>
<h3 id="remaining-tasks">Remaining tasks</h3>
<ul>
<li>Land backend APIs from <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3585354" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3585354</a></span>.</li>
<li>Extend <code>DataDependencies</code> TypeScript type.</li>
<li>Create <code>entityReferenceApi</code> RTK Query service and register it in <code>store.ts</code>.</li>
<li>Build the four new components with error-state handling for 403s.</li>
<li>Add "Bind to entity fields" action to <code>Props.tsx</code>.</li>
<li>Vitest coverage for: Save dispatches correctly-shaped payload, Cancel does not dispatch, empty selections prune keys, dialog initialises from existing Redux<br>
selection, reference-field expansion triggers <code>getFields</code> with the correct <code>parent</code>.</li>
<li>Manual verification: open the JS component editor, configure <code>entityFields</code> for a prop, save, reload — selections persist.</li>
</ul>
<h3 id="user-interface-changes">User interface changes</h3>
<p> New modal dialog accessible from the props panel in the code component editor. No changes to existing views.</p>
<h3 id="api-changes">API changes</h3>
<p> None. This issue consumes the endpoints added in <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3585354" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3585354</a></span> and writes via the existing auto-save mutation.</p>
<h3 id="data-model-changes">Data model changes</h3>
<p> None server-side. Client-side <code>DataDependencies</code> TypeScript type gains an optional <code>entityFields</code> key.</p>
> Related issue: [Issue #3585354](https://www.drupal.org/node/3585354)
> Related issue: [Issue #3573831](https://www.drupal.org/node/3573831)
issue