Repository navigation
feat(workflow-form): share the operator property panel's field-type rules - #8436
Conversation
Automated Reviewer SuggestionsBased on the
|
…ules Extract the operator property panel's custom-widget decisions into one pure helper, customFormlyFieldType (file picker, model picker, HuggingFace uploaders, dataset selector, code box, drag-reorder list), and have the panel call it. Behaviour is unchanged -- the same widget for every property -- but the mapping is now a single source of truth a later view (the Form View) can share, so a selectable or uploadable property never silently degrades to a plain text box there. Add the companion rule NON_FORM_FIELD_TYPES (the code editor): a property whose control cannot be a form field is not offered for exposure, since writing code is not filling in a value. A drag-reorder property stays exposable (it just renders without the drag). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cc8d116 to
985e09e
Compare
|
/request-review: @mengw15 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8436 +/- ##
=========================================
Coverage 94.02% 94.03%
Complexity 4820 4820
=========================================
Files 1207 1208 +1
Lines 49207 49219 +12
Branches 5978 5982 +4
=========================================
+ Hits 46268 46282 +14
Misses 1465 1465
+ Partials 1474 1472 -2
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🔵 Needs a closer look
Update the PR description and provide the required before/after screenshot for the exposure-control UI change.
Pull request overview
Centralizes Formly widget selection and prevents code-editor properties from being exposed as form fields.
Changes:
- Adds and tests shared custom-field mapping rules.
- Reuses those rules in the property editor.
- Removes exposure controls from code-editor fields.
File summaries
| File | Description |
|---|---|
frontend/src/app/workspace/util/custom-formly-type.ts |
Defines shared widget and exposability rules. |
frontend/src/app/workspace/util/custom-formly-type.spec.ts |
Tests widget mappings, guards, and defaults. |
frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts |
Applies shared rules and excludes code editors from exposure. |
frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.spec.ts |
Verifies code-editor exposure behavior. |
Review details
Suppressed comments (1)
frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts:1371
- This condition visibly removes the exposure checkbox from code-editor fields, so the PR is not entirely a “no visible change” refactor as described. Please update the PR description and add the required before/after screenshot for this authoring-state UI change.
const isNonFormField = typeof fieldType === "string" && NON_FORM_FIELD_TYPES.has(fieldType);
if (typeof topLevelField.key === "string" && !isNonFormField) {
- Files reviewed: 4/4 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
mengw15
left a comment
There was a problem hiding this comment.
I walked all nine extracted rules against their inline originals — the guards, their relative order, and the currentType capture point are equivalent, so the refactor half is faithful. One housekeeping note, seconding Copilot's screenshot ask: "no visible change" isn't quite true with the flag on — choose mode stops offering the checkbox on a code-editor property, which is the intended exposability rule; worth a one-line description touch-up (and a screenshot of that panel if cheap).
|
This PR's own goal is that a property renders the same control in both views, so I want to question the two planned exceptions. (1) The code box is banned from exposure entirely — I'd rather drop that and let the author decide: choosing what to expose is already the author's call, property by property. The render slice also has to handle an exposed code key anyway (formBinding is data — an older client or a hand-edited config can put one there), so blocking it at choose time doesn't actually protect the form. (2) The description says Projection's reorder list will render in the form without the drag — why not with it? The widget sits in the same global formly registry as every other one here; the form only needs to wire its own reorder callback, the same way the panel wires its own. If both properties just render their real widgets, neither special rule is needed. I'd like to settle this before merging. |
|
@mengw15. Agreed on the principle, and I want the same end state. The catch is that
Your data point stands and is already handled: an exposed code key from an For the MVP I would keep both as clean fallbacks and add the real in-form code |
|
Sounds good — I checked the code box and it is indeed a button opening Monaco into the workspace's editor host, so I agree that is a feature of its own, not just wiring. The fallback MVP plus a follow-up issue works for me. One ask: put removing |
…pache#8437) ### Purpose Closes apache#8370. Part of the Form View stack (apache#8011), stacked on apache#8436 (PR9). The Form View page already has the title bar and the collapsible read-only workflow preview. This PR fills in the middle: it renders the inputs an author exposed and writes filled-in values straight back to their operators. ### Changes - Each exposed binding is turned into its operator's own ngx-formly field, built from the operator's JSON schema (not guessed from the value), so a file property gets the real file picker and an attribute property a column dropdown instead of degrading to a plain text box. - The widget is decided by the shared `customFormlyFieldType` (extracted in apache#8436). Two widgets that only work on the operator canvas, the code editor and the drag-reorder list, are collected in a new `CANVAS_ONLY_FORMLY_TYPES` and fall back to formly's default editable control, since they cannot function on a form. - A changed value writes straight back to its operator, the same edit the canvas makes, guarded so formly's build-time empty default never silently wipes a real value. Only a viewer with write access can edit; a read-only viewer sees the values disabled and the page never persists for them. - The inputs rebuild on the compilation stream (attribute boxes become dropdowns once upstream columns are known) and on `formBindingChanged$` (exposing or un-exposing a property, a co-editor's change included, is reflected at once), both skipped while the cursor is in a field so an in-progress edit is not thrown away. - Broken bindings (the operator was deleted, or the key no longer exists) are left out of what a reader sees. Nested and array sub-field overrides are added in the next PR (apache#8022); running the workflow and showing results follow after that. ### Tests - `workflow-form.component.spec.ts`: direct-construction unit tests for rendering, widget selection, the write-back guards, read-only locking, the compilation/binding rebuild triggers, and the typing guard, for both the single-user and the collaboration paths. - `workflow-form.rendered.spec.ts`: TestBed test standing up the real template so the inputs markup (section head, empty state, input card and form wrapper) is covered. - `custom-formly-type.spec.ts`: covers the new `CANVAS_ONLY_FORMLY_TYPES`. - 100% statement and function coverage on the changed source. `ng test` (71 tests here), `ng build gui`, eslint and prettier all pass. ### Was AI used? Yes, co-authored with Claude (Claude Code). #### Screenshot The **Inputs** area: each exposed property rendered as its operator's own control (the workflow preview and mini-map below it are the earlier PR's). <img width="1267" height="391" alt="Screenshot 2026-09-05 at 11 41 41 AM" src="https://github.com/user-attachments/assets/4572fa60-3124-4140-8f7c-ddd7f22133fd" /> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…#8438) ### What changes were proposed in this PR? Closes apache#8022. Part of the Form View stack (parent issue apache#8011), stacked on apache#8437 (PR10) and apache#8436 (PR9). The render PR (apache#8437) shows the top-level exposed inputs. This one handles a property that carries sub-fields: an object (a nested group) or a repeated section (an array). It renders each sub-field and applies the author's per-sub-field setup. - A walk over the built formly field and its sub-fields drops the operator schema's own per-field descriptions (author notes about the operator, not guidance to a form reader, and shown once per field by formly), and applies the author's stored overrides -- rename and hide -- keyed by field path, with array indices dropped so one entry covers every row. - A repeated section builds its row template on demand, so the walk wraps the builder rather than the single object it returns, decorating every row formly ever creates. - Two small statics, `childPath` (the override path for a child, indices dropped) and `arrayItemOf` (a repeated section's row template, whether formly gives it as a value or a builder), back the keying and are unit tested directly. Writing a filled-in value back to its operator is already handled by apache#8437: formly's model carries the nested value, so a sub-field edit persists through the same write-back. This slice is the sub-field rendering and overrides only. ### Any related issues, documentation, discussions? Closes apache#8022. Part of the Form View feature (parent issue apache#8011). ### How was this PR tested? Unit tests (vitest). `sub-fields.spec.ts` covers the two statics directly (path joining, index dropping, non-name keys; the value/builder/throwing/none row-template cases). `workflow-form.component.spec.ts` covers the walk against the component: renaming and hiding an overridden sub-field of both an object property and a repeated section (per row, through the wrapped builder), dropping the schema descriptions, leaving an un-overridden sub-field untouched, and a scalar array's leaf row template. 100% statement and function coverage on the changed source; the remaining uncovered branches are defensive `??`/`||` fallbacks. `ng build gui`, eslint and prettier are clean, and the full workflow-form suite (73 tests) is green in both the single-user and collaboration paths. The visible effect (with the flag on) is that an exposed nested or array property renders its sub-fields, each renamed or hidden as the author set up. A screenshot can be added on request. #### Screenshot An exposed nested or array property rendered with its **sub-fields**, each renamed or hidden as the author set up. <img width="1271" height="785" alt="Screenshot 2026-09-05 at 11 42 53 AM" src="https://github.com/user-attachments/assets/62233d72-c64b-421d-b637-2dce4e742405" /> ### Was this PR authored or co-authored using generative AI tooling? Yes. Co-authored with Claude (Anthropic), reviewed line by line by the author before submission. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Resolves conflicts with the Form View work (apache#8318, apache#8429, apache#8436, apache#8442): migrates the new paintCurrentOperatorState repaint to the split state/statistics API and drops the unused OperatorStatistics import the upstream baseline carried in the property editor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…m, with the drag (apache#8843) ### What changes were proposed in this PR? Closes apache#8761. Part of the Form View feature (apache#8011); from the apache#8439 refinements (item 2). An exposed drag-reorder property -- Projection's columns, formly type `repeat-section-dnd` -- fell back to a plain repeated list in the Form View (`CANVAS_ONLY_FORMLY_TYPES`): a reader could edit the rows but not reorder them, which is what the widget is for. The fallback assumed the drag had nowhere to attach on a form; the widget carries its own CDK drop list, and the row template it needs comes from the operator's own schema (the per-instance one, with the upstream columns), which the form already builds. - `repeat-section-dnd` leaves `CANVAS_ONLY_FORMLY_TYPES`; the set now holds the code editor only. On the form the widget renders exactly as on the operator property panel, drag handle and all, and a drop reaches the operator through the card's `valueChanges` write-back; the panel's `reorder` callback stays optional (the widget calls it only when the host provides one), so the same widget serves both hosts without a form-specific branch. Nothing is added to the widget's UI. - Two defects in the widget itself, found while reviewing this and in reach of the form for the first time: every button now has `type="button"` -- `nz-button` sets no type, so inside a form (the card's, the panel's) a typeless button is the submit button and Enter in a row's input clicked it, removing the first row (the fallback the form used before had typed buttons; the panel has had this since the widget exists); and the rows are re-keyed to their positions after a drop, as formly's own `remove()` does -- formly keys rows by position and writes a cell's value into the model by that key path, so a value typed into a moved row landed on the row that used to be there (the same after a drag on the panel, until the operator was re-selected). ### Any related issues, documentation, discussions? Closes apache#8761. Raised by @mengw15 on apache#8436; filed from apache#8439. Builds on the sub-field walk from apache#8438. apache#8760 (proper) also shrinks `CANVAS_ONLY_FORMLY_TYPES`; whichever lands second deletes the then-empty set. Known limitation, not this PR's code: the Form View rebuilds every card on every compilation, including the one a card's own write triggers, so on the form a drop's write redraws the rows about a second later, and a second drop made in the last 150 ms before that redraw is undone. The canvas panel has no such rebuild. Filed as apache#8844 and fixed in apache#8845 (the form rebuilds only when the schema, the set of inputs or a value it does not hold changed); this PR is independent of it. Other follow-ups: a card's model is seeded once and never re-seeded from a co-editor's edit of the same property, so a reorder writes the whole array back over such an edit (pre-existing for every array/object property; a reorder is one more trigger); the HuggingFace and codearea widgets also carry typeless buttons. ### How was this PR tested? Unit tests (vitest). The widget's spec: a drop reorders the model, the field configs and the form controls and then tells the parent; a section without a reorder callback still reorders; a keyless row's children are re-keyed to the row's new position, as formly's own remove() does; every button carries `type="button"`; the existing rendering tests (one row per entry, draggable rows, remove, add, disabled section) still hold. A second spec stands the widget up under real formly (`FormlyModule.forRoot`, a form, a model): a drop changes the model and the cells and keeps the rows' keys in step with their positions, and a value typed into a moved row lands on that row's entry. The form's spec: Projection's `attributes` renders through `repeat-section-dnd`. `custom-formly-type`'s spec pins the set. Each guard was deletion-checked (the re-keying dropped; the callback called unguarded; the remove button typeless; the type back in the canvas-only set) and turned a named test red. eslint, prettier and the production (AOT) build pass; the full frontend suite passes; every statement, function and branch this PR adds is covered. Checked on a flag-on instance (main-demo; a Python source with four columns feeding a Projection whose `attributes` is exposed), with the canvas and the form open side by side: on main the card is a plain list (no handle); with this PR the card shows the drag handles, as the panel does. Dragging the last row to the top: the operator's order follows in the canvas tab's panel within a second and in the saved workflow. An alias typed into the moved row lands on that column, not its neighbour; Enter in an alias input removes no row; a reload shows the same order. A drop made on the canvas panel reaches the form within a second. #### Video **Before** https://github.com/user-attachments/assets/5a78f6f5-3b27-4b93-955e-bb15b40fd854 **After** https://github.com/user-attachments/assets/b84c79fa-a0a4-4bbc-b734-e75147bdf0d6 ### Was this PR authored or co-authored using generative AI tooling? Yes. Generated-by: Claude Code (Claude Fable 5.1, Anthropic). Co-authored with Claude, reviewed line by line by the author before submission. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Meng Wang <mengw15@uci.edu>
What changes were proposed in this PR?
Field-type infrastructure the Form View reader will build on (parent issue #8011). Frontend only. The refactor half is behaviour-preserving; the one exposability rule it adds has a single visible effect, and only with the feature flag on: in the authoring panel, a code-editor property no longer offers the expose checkbox (an ordinary property still does).
customFormlyFieldType, and have the panel call it. Same widget for every property as before; the mapping is now a single source of truth a later view (the Form View) can share, so a selectable or uploadable property never silently degrades to a plain text box there.NON_FORM_FIELD_TYPES(the code editor): a property whose control cannot be a form field is not offered for exposure, since writing code is not "filling in a value". A drag-reorder property (e.g. Projection's columns) stays exposable -- it just renders without the drag in the form.Sub-field addressing (
childPath/arrayItemOf) is intentionally not here: it lives on the form component and lands with the render PR that consumes it.Any related issues, documentation, discussions?
Closes #8021. Part of the Form View feature (parent issue #8011); builds on the merged panel/choose work.
How was this PR tested?
Unit tests (vitest).
custom-formly-type.spec.tscovers the helper at 100% (statements and branches) -- every widget rule, each guarded negative, and the default. The sharedoperator-property-edit-frame.component.spec.ts(250 tests) is green, confirming the extraction leaves the panel's behaviour unchanged, plus a new test that a code-editor property is not offered for exposure while an ordinary one is. Every line added to the panel is covered.ng buildis clean.The one visible effect (with the flag on): in the authoring panel a code-editor property no longer offers the expose checkbox, while an ordinary property still does. Screenshot available on request.
Was this PR authored or co-authored using generative AI tooling?
Yes. Co-authored with Claude (Anthropic), reviewed line by line by the author before submission.