feat(action,track): add a reserved mounted pseudo-event and generic $event paths - #658
Open
titouanmathis wants to merge 3 commits into
Open
titouanmathis wants to merge 3 commits into
titouanmathis wants to merge 3 commits into
Conversation
Let HTML run an Action once its element and the components sharing it are mounted, without binding the public component lifecycle DOM event. `Track` already deferred its own `mounted` declaration that way. Lift the scheduling into `utils/mounted-event.ts` and have both families use it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LMSCm41fm3g7chxD728vAu
…atch Placeholders now resolve against the whole event, so `$event.target.dataset.type` works on a native event and `$event.detail.response.headers.x-count` on a component event. `$detail.x` is rewritten to the path `detail.x` walked from the same root. The inherited context, the payload script and the payload option are read when the event fires, so a partial DOM update changes what the next dispatch sends. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LMSCm41fm3g7chxD728vAu
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LMSCm41fm3g7chxD728vAu
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## feat/v2-fetch-lifecycle-detail #658 +/- ##
=================================================================
Coverage 86.32% 86.32%
Complexity 145 145
=================================================================
Files 20 20
Lines 746 746
Branches 88 88
=================================================================
Hits 644 644
Misses 95 95
Partials 7 7
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacks on #657, which stacks on #656. Review the last two commits only until those land; GitHub retargets this PR as the stack merges.
#648 — a reserved
mountedpseudo-event forActiondata-on:mountedruns the effect once per mount cycle, after the current mount batch has settled, so the effect can target a component mounted on the same element:It binds no DOM listener, so a lifecycle event bubbling from a descendant that mounts later never runs it again. Unmounting before the deferred effect runs cancels it, and remounting starts exactly one new one. Both halves of the component go through it: the
data-on:*namespace and theon/target/effectoption triple.The effect receives
undefinedfor itseventargument.debounceapplies;prevent,stop,once,captureandpassivehave nothing to act on and are documented as ignored.Where the shared scheduling lives
packages/ui/src/utils/mounted-event.ts, next toevent-modifiers.ts. It holds the reserved name andwhenMounted(component, callback), which posts the work todefaultScheduler.background, guards on$isMounted, and returns its cancel.AbstractTrack.__bind()and the newAction.__attach()both use it.Track's__deferredset is gone rather than moved:watchAttributeNamespace's cleanup releases every binding, and each binding's release already cancels its own task, so the set cancelled nothing the release had not. Behaviour is unchanged — the existingTrackspecs cover the cancel-on-unmount and remount paths.#652 — generic
$eventpaths, and fresh DOM-backed data$event.<path>resolves against the whole event, so a native event works too:Resolution no longer requires a
CustomEvent. The.detailmodifier, which merges the detail wholesale, staysCustomEvent-only, because a native event has none.$detailis kept as sugar, as the maintainer asked. It is a path rewrite, not a second code path:$detail.xbecomes the pathdetail.xwalked from the event, so the two roots can never disagree. That rewrite also removed the special case for a non-object detail — descending into a primitive already yieldsundefined, which is the same answer the special case gave. The change is therefore non-breaking, so nothing goes in the migration guide.The resolver walks objects and arrays, reads a numeric segment as an array index (
request.searchParams.genre.0), recurses through nested objects and arrays in the declared payload, returnsundefinedfor a missing path and for every placeholder when there is no event at all, and knows nothing aboutFetchor any other emitter.resolveDetailPlaceholdersis renamedresolveEventPlaceholders, updated inpackages/ui/src/Track/index.ts. It is not in the package root barrel, sopublic-contracts.tsand the barrel snapshot are unaffected.Fresh DOM-backed data
AbstractTrackno longer caches__payloadand__context. The inheritedTrackContext, the payload script and the payload option are read when the event fires, so aFetchthat morphs a JSON script or a context attribute changes what the next dispatch sends, with no remount. Merge precedence is unchanged.Tests
packages/tests/Action/Action.spec.ts— runs once after mount; reaches a component on its own element; binds no listener; ignores amountedand ajs-toolkit:component:mountedevent bubbling from a descendant; cancels on unmount; one new effect per remount;undefinedevent;debounce; theonoption.packages/tests/Track/TrackEvent.spec.ts—$event.*on aCustomEvent, a native event and a component event;$detail.*equal to$event.detail.*; numeric segments; missing path; no event; nested objects and arrays.packages/tests/Track/Track.spec.ts— a morphedTrackContextscript, a morphed payload script and a rewritten payload option each affect the next dispatch on the same instance; precedence unchanged when a source changes.packages/tests/Track/TrackFetch.spec.ts— a realFetchupdate read throughdata-track:fetch-update-after, resolving a response header, the request method, a repeated search param by index, and a missing header.npm run test(1037 passed),npm run lint,npm run manifest:checkandnpm run docs:buildall pass.Closes #648
Closes #652
🤖 Generated with Claude Code
https://claude.ai/code/session_01LMSCm41fm3g7chxD728vAu