SIGN IN SIGN UP

fix(frontend): gate the add component affordance on the pending permission check (#14523)

* refactor(frontend): expose the pending half of the flow permission gate

useIsFlowReadOnly conflates two states: the query is still resolving, and
the user is denied. Controls that invoke a gated mutation need to tell them
apart to disable themselves only while the answer is in flight and to name
the reason correctly.

Adds useIsFlowPermissionPending next to it, reading the same provider so
both predicates can never disagree. No behaviour change on its own.

* fix(frontend): gate sidebar component items on the pending permission check

The add path already refused to run while the permission query resolved,
but the item stayed draggable, double-clickable and kept an enabled add
button, so the click was discarded with nothing reported. The row now
carries the same not-allowed affordance it already uses for placement
constraints, and the add button is disabled for that window.

A constraint violation is a verdict and still hides the button; a pending
check is transient, so it only suspends it and the control keeps its place.

Refs LE-2176

* fix(frontend): gate New Custom Component on the pending permission check

Same discarded-click window as the component items, on the sidebar footer
button. It already had a disabled channel wired to the component-types
load, so the pending permission state joins it.

Because the button opts out of the base class with unstyled, the disabled
look is spelled out at the call site — without it the control would block
the click while still looking exactly like a working one.

Refs LE-2176

* test(frontend): cover the add affordance during the pending permission check

Splits the two halves of the gate on the hook, then asserts on both add
surfaces that the control refuses the click visibly instead of discarding
it, and that a placement constraint still wins the reason when both apply.

Checked against the base: 5 of the 8 item assertions and 3 of the 4 button
assertions fail without the fix; the rest are the resolved-state controls
and the constraint regression guard.

Refs LE-2176

* test(frontend): add e2e guard for the discarded add click

Holds the permission response open so the window is deterministic instead
of racing a round trip that is milliseconds wide on localhost, then asserts
the control is unavailable during it and that the same single click adds a
node once the answer arrives.

Asserts the request actually happened: without that the test would pass
vacuously on any build where the editor stops querying permissions.

Refs LE-2176

* test(frontend): poll the intercepted permission request instead of reading it once

The counter is incremented by the route handler on the Node side, while the
visibility assertion it follows resolves on the browser side. Nothing orders
the two, so on a loaded runner the guard could read zero and fail a test that
is otherwise correct.

Addresses the CodeRabbit review on #14523.

* fix(frontend): dim the custom component button for both refusal reasons

The button was disabled by the component-types load and by the pending
permission check, but only the second one dimmed it. Same control looking
refused for one cause and working for the other reads as a bug, and the
load case fires on every flow open, so it is the more frequently hit half.

Hoists the condition so disabled and the disabled look cannot diverge.

Addresses I1 of the review on #14523.

* fix(frontend): refuse the add affordance for denied write, not just in flight

The gate the add path uses is read-only, which is true both while the
permission answer travels and, permanently, when it denies write. Gating the
controls on the pending half alone left a read-only collaborator with a fully
interactive sidebar whose every add is discarded, with nothing reported — the
same defect, except it never resolves.

Both surfaces now key off useIsFlowReadOnly, the exact predicate that refuses
the click, and useIsFlowPermissionPending only picks which reason to show:
still checking, or read-only access. A placement constraint keeps hiding the
add button; the permission cases only disable it, so a denied user still sees
the layout everyone else does.

Addresses I2 of the review on #14523.

---------

Co-authored-by: Eric Hare <ericrhare@gmail.com>
T
Tarcio committed
0a2d167ade70e7ed43187090212ddaf75ac57c97
Parent: e0859c4
Committed by GitHub <noreply@github.com> on 8/14/2026, 9:20:44 PM