Validate component instancing against the live instance #208

Closed
opened 2026-09-14 23:13:27 +00:00 by david · 1 comment
Owner

Summary

Validate component instancing against the live instance and the Penpot UI: instantiate a library component into a screen, confirm the UI treats it as a real instance, and confirm that editing the main component propagates to the instance — the property that distinguishes an instance from a copy.

Background

Depends on: #207

The field contract (componentId, componentRoot, shapeRef) was already proven to persist, but persistence in the data model is not the same as behaving as an instance in the UI. This gate closes that gap: if editing the main component does not update the instance, the feature delivers copies with instance-looking metadata, and that must be discovered and reported here rather than discovered by a user later.

Required environment: PENPOT_URL, PENPOT_TOKEN, the scratch project, the committed library artifact imported into it, and a consumer file linked to that library.

Documentation Required

A separate process downloads these into the listed folders before this issue is implemented. Check the folders for the actual reference material before starting.

docs/reference/penpot-api/

Implementation Details

  1. Instantiate. On a consumer file linked to the imported library, stage a frame plus an instance of the Button component inside it, then commit.
  2. Inspect the persisted data. Re-read both files and confirm:
    • the instance root carries componentId and componentRoot: true;
    • each child carries shapeRef pointing at the corresponding main-instance child id;
    • the ids are all new (no collisions with the main instance or with existing shapes);
    • asset references still point at the library's colours/typographies.
  3. Confirm in the UI. Open the consumer file:
    • the instance renders with the component's appearance inside the frame;
    • its properties panel identifies it as an instance of the component (not a frame/group);
    • the Layers panel shows the requested instance name.
  4. Prove the linkage. Edit the main component in the library file (e.g. change its fill to another library colour, or its corner radius). Then, in the consumer file, refresh/update the instance as the UI requires and confirm the instance follows the main component. Record exactly what step (if any) the UI required — for example an explicit "update" action in the assets panel or on the instance.
  5. Overrides. Change something local to the instance (e.g. its text) and confirm it survives a main-component edit that does not touch that property — and record whether the override survives or is reset. Honest reporting matters more than a clean result.
  6. Removal behaviour. Unlink the library (penpot_unlink_library) on a throwaway consumer file and record what happens to the instance — the design intends the instance's own geometry to persist, but confirm rather than assume.

Record in extensions/penpot/findings.md under "Component instancing": the staged vs persisted field values, the UI observations for each check, the exact steps the UI required to propagate a main-component edit, what survived as an override, and anything that did not work.

Acceptance Criteria

  • An instance of the imported Button component is created inside a frame on the consumer file and renders correctly in the Penpot UI.
  • Persisted data confirms componentId + componentRoot: true on the instance root and shapeRef on every child, with all-new ids (no collisions).
  • The Penpot UI identifies the object as an instance of the component in its properties panel.
  • Editing the main component's appearance is reflected in the instance (after whatever refresh step the UI requires), and that step is recorded exactly.
  • Local overrides survive a compatible main-component edit, or the exact reset behaviour is recorded as a limitation.
  • Unlinking the library leaves the instance's geometry intact (or the observed behaviour is recorded).
  • extensions/penpot/findings.md contains the evidence for every check, including negative results, and states plainly whether instancing behaves as a true instance on Penpot 2.17.
  • Any defect found in the instancing primitive is fixed in code, not merely noted.

Test Plan

Manual, against the live instance:

# in pi
/reload
# penpot_authorize_target(project_id=<scratch project>)
# penpot_add_frame(file_id=<consumer>, ...)
# penpot_instance_component(file_id=<consumer>, component_name="Button", parent_id=<frame id>, x=24, y=24)
# penpot_commit(file_id=<consumer>)
# penpot_get_file(file_id=<consumer>, include_objects=true)   -> inspect componentId/componentRoot/shapeRef

Then in the Penpot UI:

  1. Open the consumer file; confirm the instance renders and the properties panel calls it an instance.
  2. Open the library file; change the main Button's fill; save.
  3. Return to the consumer file; perform the update step the UI requires; confirm the instance followed.
  4. Change the instance's text locally; re-edit the main component; confirm whether the override survived.
## Summary Validate component instancing against the live instance and the Penpot UI: instantiate a library component into a screen, confirm the UI treats it as a real instance, and confirm that editing the **main component** propagates to the instance — the property that distinguishes an instance from a copy. ## Background **Depends on:** #207 The field contract (`componentId`, `componentRoot`, `shapeRef`) was already proven to persist, but persistence in the data model is not the same as behaving as an instance in the UI. This gate closes that gap: if editing the main component does not update the instance, the feature delivers copies with instance-looking metadata, and that must be discovered and reported here rather than discovered by a user later. Required environment: `PENPOT_URL`, `PENPOT_TOKEN`, the scratch project, the committed library artifact imported into it, and a consumer file linked to that library. ## Documentation Required A separate process downloads these into the listed folders before this issue is implemented. Check the folders for the actual reference material before starting. **`docs/reference/penpot-api/`** - https://help.penpot.app/user-guide/design-systems/components/ — the designer-facing contract being validated: main instances, instances, updating instances after a main-component edit, and overrides. This is the source of the expected behaviour. - https://help.penpot.app/user-guide/design-systems/libraries/ — how a linked library's components behave for a consumer, and what happens if the link is removed. - https://raw.githubusercontent.com/penpot/penpot/2.17.2/common/src/app/common/types/component.cljc — the component record, for interpreting anything the instance's data shows. - https://raw.githubusercontent.com/penpot/penpot/2.17.2/common/src/app/common/types/shape.cljc — `componentId`/`componentRoot`/`shapeRef`, to confirm the persisted values match what was staged. - `<PENPOT_URL>/api/main/doc/openapi.json` — `get-file` on both the library file and the consumer file, to compare main instance and instance subtrees. ## Implementation Details 1. **Instantiate.** On a consumer file linked to the imported library, stage a frame plus an instance of the `Button` component inside it, then commit. 2. **Inspect the persisted data.** Re-read both files and confirm: - the instance root carries `componentId` and `componentRoot: true`; - each child carries `shapeRef` pointing at the corresponding main-instance child id; - the ids are all new (no collisions with the main instance or with existing shapes); - asset references still point at the library's colours/typographies. 3. **Confirm in the UI.** Open the consumer file: - the instance renders with the component's appearance inside the frame; - its properties panel identifies it as an instance of the component (not a frame/group); - the Layers panel shows the requested instance name. 4. **Prove the linkage.** Edit the **main component** in the library file (e.g. change its fill to another library colour, or its corner radius). Then, in the consumer file, refresh/update the instance as the UI requires and confirm the instance follows the main component. Record exactly what step (if any) the UI required — for example an explicit "update" action in the assets panel or on the instance. 5. **Overrides.** Change something local to the instance (e.g. its text) and confirm it survives a main-component edit that does not touch that property — and record whether the override survives or is reset. Honest reporting matters more than a clean result. 6. **Removal behaviour.** Unlink the library (`penpot_unlink_library`) on a throwaway consumer file and record what happens to the instance — the design intends the instance's own geometry to persist, but confirm rather than assume. Record in `extensions/penpot/findings.md` under "Component instancing": the staged vs persisted field values, the UI observations for each check, the exact steps the UI required to propagate a main-component edit, what survived as an override, and anything that did **not** work. ## Acceptance Criteria - [ ] An instance of the imported `Button` component is created inside a frame on the consumer file and renders correctly in the Penpot UI. - [ ] Persisted data confirms `componentId` + `componentRoot: true` on the instance root and `shapeRef` on every child, with all-new ids (no collisions). - [ ] The Penpot UI identifies the object as an **instance** of the component in its properties panel. - [ ] Editing the main component's appearance is reflected in the instance (after whatever refresh step the UI requires), and that step is recorded exactly. - [ ] Local overrides survive a compatible main-component edit, or the exact reset behaviour is recorded as a limitation. - [ ] Unlinking the library leaves the instance's geometry intact (or the observed behaviour is recorded). - [ ] `extensions/penpot/findings.md` contains the evidence for every check, including negative results, and states plainly whether instancing behaves as a true instance on Penpot 2.17. - [ ] Any defect found in the instancing primitive is fixed in code, not merely noted. ## Test Plan Manual, against the live instance: ```bash # in pi /reload # penpot_authorize_target(project_id=<scratch project>) # penpot_add_frame(file_id=<consumer>, ...) # penpot_instance_component(file_id=<consumer>, component_name="Button", parent_id=<frame id>, x=24, y=24) # penpot_commit(file_id=<consumer>) # penpot_get_file(file_id=<consumer>, include_objects=true) -> inspect componentId/componentRoot/shapeRef ``` Then in the Penpot UI: 1. Open the consumer file; confirm the instance renders and the properties panel calls it an instance. 2. Open the library file; change the main `Button`'s fill; save. 3. Return to the consumer file; perform the update step the UI requires; confirm the instance followed. 4. Change the instance's text locally; re-edit the main component; confirm whether the override survived.
david closed this issue 2026-09-15 05:49:31 +00:00
Author
Owner

pi-loop opened and merged a pull request for this issue: #241

pi-loop opened and merged a pull request for this issue: https://git.excelera.net/david/pi-extensions-and-skills/pulls/241
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
david/pi-extensions-and-skills#208
No description provided.