Skip to content

feat(plugin): add window popup plugin to record popup events#1900

Open
smeng9 wants to merge 13 commits into
rrweb-io:mainfrom
smeng9:feat/dialog-plugin
Open

feat(plugin): add window popup plugin to record popup events#1900
smeng9 wants to merge 13 commits into
rrweb-io:mainfrom
smeng9:feat/dialog-plugin

Conversation

@smeng9

@smeng9 smeng9 commented Jul 14, 2026

Copy link
Copy Markdown

Record window.alert / window.confirm / window.prompt events as part of UI interaction

Shaoyu Meng and others added 13 commits July 13, 2026 13:42
Design for a plugin pair that captures native window.alert/confirm/prompt
dialogs (message, prompt default, user response) and surfaces them on
replay via a callback, mirroring the console/network plugins.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Native window.alert/confirm/prompt dialogs are browser chrome outside the
DOM, so rrweb never captured them. This adds a plugin pair mirroring the
console/network plugins:

- @rrweb/rrweb-plugin-dialog-record patches alert/confirm/prompt via
  @rrweb/utils' patch(), calling through to the real (blocking) dialog
  first so it can capture the user's response, then emitting a
  'rrweb/dialog@1' plugin event with { kind, message, defaultValue?,
  returnValue? }. Options: level, recordReturnValue, maskDialog.
- @rrweb/rrweb-plugin-dialog-replay filters those events and surfaces
  each dialog to an onDialog callback (callback-only, since re-showing a
  blocking native dialog would freeze the replayer).

Covered by unit tests (mock-window observer pattern); both packages
type-check and build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename @rrweb/rrweb-plugin-dialog-record/replay to
@rrweb/rrweb-plugin-popup-record/replay, including the shared plugin
identifier ('rrweb/dialog@1' -> 'rrweb/popup@1') and all exported
symbols (getRecordPopupPlugin, getReplayPopupPlugin, PopupData,
PopupKind, PopupRecordOptions, PopupReplayOptions, onPopup, maskPopup).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop `?? {}` from the factory to match the network plugin's style;
normalize undefined options inside the observer instead, and type the
factory return as RecordPlugin (as network does) so options may be
undefined.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`level` was a meaningless carry-over from the console plugin (where it
denotes log severity). Here the option holds a PopupKind[] (alert /
confirm / prompt), so `kinds` names it accurately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
recordReturnValue was redundant: dropping the response is just a special
case of masking. Remove it, always capture returnValue, and rename
maskPopup -> maskPopupData. Users redact or drop the return value via the
mask hook; documented with an example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite both popup plugin READMEs to the standard rrweb plugin format
(pointer to the recipe + guide, followed by the shared sponsors/team
block), matching rrweb-plugin-console-record and rrweb-plugin-network-record.
Move the usage/options docs into docs/recipes/popup.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The patched popup's receiver is always the recorded window, so apply to
`win` directly and use an arrow function instead of capturing the
caller's `this`. Removes the `this: unknown` annotation and is slightly
more robust (correct Window receiver even inside iframes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
IWindow (= Window & typeof globalThis) already satisfies patch()'s
{ [key: string]: any } source parameter, as network-record does with
patch(win, 'fetch', ...). Pass win directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…iate

options is undefined at runtime when the plugin is created with no args
(getRecordPopupPlugin() -> factory passes options: options). Type the
observer param as optional and read via optional chaining instead of the
`options || {}` intermediate, so the signature is honest and the direct
access is safe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Design is finalized and implemented; the point-in-time spec (with its
pre-rename dialog naming) is no longer needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 189d2a6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@rrweb/rrweb-plugin-popup-record Minor
@rrweb/rrweb-plugin-popup-replay Minor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant