Repository navigation
fix(ui): deduplicate shadow ancestor events - #439
Conversation
🦋 Changeset detectedLatest commit: 306c26a The changes in this PR will be included in the next version bump. This PR includes changesets to release 0 packagesWhen changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9f38af0868
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| button.dispatchEvent(keyDownEvent); | ||
|
|
||
| await expect(outsideKeyDowns).toHaveLength(2); | ||
| await expect(new Set(outsideKeyDowns.map(({ event }) => event))).toHaveLength(1); |
There was a problem hiding this comment.
Compare Set size instead of length
When this story runs in either Storybook test job from .github/workflows/storybook.yml, this assertion fails regardless of event behavior: a Set exposes .size, not .length, while Vitest's toHaveLength requires a numeric .length property. Compare the set's size with toBe(1) instead, and make the same correction to the click-event assertion below.
Useful? React with 👍 / 👎.
cameronapak
left a comment
There was a problem hiding this comment.
Review
Summary
Standards: 0 must-fix. Spec: 0 must-fix. Primary concern: none.
Review evidence
- Scope: reviewed revision
85b1321, YPE-6040, the shared host implementation, consumer workflows, documentation, and existing review threads. - Method: traced React and native event delivery for click, keydown, focus, repeated event objects, host-origin events, and nested roots.
| Behavior or check | Method / command | Result | Evidence source |
|---|---|---|---|
| Cross-browser consumer workflow | Storybook CI | Chromium, Firefox, and WebKit passed | CI run |
| Build, lint, types, tests, and bundle size | CI | Passed | CI run |
Set.toHaveLength concern |
Inspected and executed Storybook matcher | Valid: Chai reads Set.size; both assertions passed |
Coordinator |
- Limits: actual Safari remains intentionally deferred to YPE-5952. Capture-phase and other event types are documented non-goals.
- CI and bot review: all required checks are green. The unresolved
Set.toHaveLengthbot comment is not actionable. - Event: APPROVE.
Written by Code Reviewer bot on behalf of Cam.
| const nativeEvent = event.nativeEvent; | ||
| const host = event.currentTarget; | ||
| const origin = nativeEvent.composedPath()[0]; | ||
| const isRetargetedHostTraversal = nativeEvent.target === host && origin !== host; |
There was a problem hiding this comment.
praise: This predicate narrowly identifies the retargeted second React traversal while preserving host-origin events and native propagation.
For Agents: precise traversal boundary
Comparing the retargeted native target with the original composed-path node avoids caching event objects or suppressing redispatch. The cross-browser workflow covers repeated native objects, distinct clicks, host-origin key events, and nested roots.
Written by Code Reviewer bot on behalf of Cam.
Summary
composedPath()behaviorJira: YPE-6040
Requirement evidence
onClick,onKeyDown, andonFocusreach a light-DOM React ancestor once per native eventKeyboardEventdispatched twice produces two component and ancestor observations.targetandcurrentTargetfor click, keydown, and focus callbacks.composedPath().onFocus/nativefocusin; the nested-root story asserts one React click delivery at each scope and native retargeting across both boundaries.ShadowRootHostevent handling; no exported prop or type changes.Review scope
Required outcomes:
Permitted support work:
Non-goals:
Please treat a finding as blocking only when it identifies an unmet in-scope requirement, a documented repository-standard violation in added or modified code, or a concrete regression or defect caused or worsened by this diff. Label other valid improvements as non-blocking follow-ups.
Verification
pnpm --filter @youversion/platform-react-ui exec vitest run --project unit --maxWorkers=1 --fileParallelism=false— 52 files, 632 tests passedpnpm typecheckpnpm lintpnpm turbo build --forceThe default parallel local
pnpm testrun encountered eight unrelated 5-second timeout-only failures in reader, popover, search, and version-picker tests after PR #433 merged. Every timed-out test passed in the green serial UI run above; core and hooks were green in the original run. CI remains the authoritative parallel run.Review audits
The changes since the last review appear safe to merge.
What we checked:
Summary
The PR prevents duplicate React ancestor calls for bubbling
click,keydown, andfocusinevents across SDK shadow roots. It adds Storybook checks for callback targets, native delivery, repeated events, and nested roots.Diagram
Reviews (7) · Last reviewed commit: "docs: clarify shadow event rollout dispo..." · Reviewed by Greptile