Skip to content

Add floating Traffic Boost actions#3445

Merged
alecgeatches merged 45 commits intodevelopfrom
add/floating-boost-link-actions
Jun 11, 2025
Merged

Add floating Traffic Boost actions#3445
alecgeatches merged 45 commits intodevelopfrom
add/floating-boost-link-actions

Conversation

@alecgeatches
Copy link
Contributor

@alecgeatches alecgeatches commented Jun 6, 2025

Description

This PR moves Traffic Boost actions from the page footer into a floating action bar:

floating-action-bar

Motivation and context

The previous location of the actions toolbar required moving between the post context to the bottom of the Traffic Boost page to interact with suggestions and links. The floating action bar layout allows actions to be completed in the place they were initiated for a faster experience.

How has this been tested?

This has been extensively tested manually. There are a few key problem areas during development that are worth additional focus in testing:

  1. Reselecting suggested text, like shown below:

    Screen.Recording.2025-06-06.at.12.20.06.PM.mov

    The action bar markup is now embedded within the highlight mark and positioned absolutely above the suggestion. This caused some issues with our highlighting logic, which could stumble on the embedded <div>s or see newlines where HTML was embedded. This lead to a number of scenarios where reselecting text containing the initial suggestion (partially or completely) caused a failure to highlight. Please try modifying suggestion text to include none, some of, or all of the original text in testing as shown above.

  2. Ensure that the bar stays within the bounds of post content during dragging, and that dragging feels natural and fluid:

    bar-move-to-bounds.mov
  3. This is a less common scenario, but we want to ensure that the floating action bar stays usable if the window is resized:

    iframe-resize.mov
  4. Ensure regular operations on the bar (accept, reject actions) and inbound links (remove action) function as before.

Technical notes

Component mounting

The actions component <PreviewActions> is created in the context of the outer document, and passed into useIframeHighlight() and mounted here. This was modeled after the existing "Use as Link Text" popover shown when text is selected.

The combination of creating a component in the window's document and mounting it within the iframe allows communication between actions taken within the iframe in the outer page. This is similar to built-in React functionality like createPortal(), but I chose to use the same technique as the popover during development.

Actions bar markup as a child of highlight span

The actions bar is positioned absolutely above the current highlight, and structured this way:

highlight-as-parent

This caused some issues that I didn't notice until late into development on this feature. Namely, the inserted HTML can cause newlines to appear in the textContent of the highlight that weren't previously there when the highlight was only a single <span>, and the document selection sometimes including more than 1 combined range. This mainly affected:

  • <TextSelectionTooltip>'s handleSelection(), which includes a check for when multiple ranges are present. This logic mainly kicks in when a user selects text that includes an existing suggestion. This also fixes an existing bug that could allow a user to select over multiple paragraphs and still improperly recieve a prompt to "Use as Link Text" when the placement was invalid.
  • <TextSelectionTooltip>'s onSelect() callback, which also has additional steps to handle multiple ranges and strip out HTML when the selected text includes an existing suggestion.
  • removeAndClean() used in the highlighting process was also modified to handle the parent container node that is added in highlight markup.

See the "Reselecting suggested text" test suggestion above for ways to test this is working.

In retrospect, this particular issue would be a good reason to implement the actions bar without absolutely positioning above the highlight text, and instead inserting it into a non-content area of the iframe HTML. However to reimplement this bar that way, we'd need to modify offset calculations, add more event listeners and logic to keep it aligned, and potentially have other positioning issues. These workarounds were a trade-off to make positioning logic easier, but there were a lot of highlight-related problems that came out of this design.

Actions bar realignment

There are two main areas we actively change action bar alignment to fit the page:

  1. <PreviewActions>'s onDrag() handler. This function receives a delta of movement when a user is dragging (e.g. x = -5, y = +2) and returns the resulting correct delta when taking into account the iframe's bounds. The logic in here keeps the actions bar within DRAG_MARGIN_PX (5px) of the outer wall of the iframe.

    EDIT: This code was modified in eaee1f8 to fix a bug that caused the bar to drag weirdly in Safari. Instead of using the reported movementX/movementY we get from the mouse event, we instead store the initial coordinates and keep a running tally of the delta. It's a pretty minor change, and it fixes the Safari issue.

  2. When the page is resized after render, this resize hook calls into adjustActionsBarPosition(). The logic here works like this:

    • First, remove the align-left and align-right CSS classes if they exist. This will place the suggestion bar centered above the suggestion:

      Screenshot 2025-06-06 at 1 08 44 PM

    • If the size of the toolbar (plus margin) is found to overflow on the right side of the page, add the align-right class. This will move the actions bar so that the trailing edge aligns with the suggestion and keep the bar on the page:

      Screenshot 2025-06-06 at 1 08 55 PM

    • If the overflows on the left, add the align-left class. This will move the actions bar so that the leading edge aligns with the suggestion and keep the bar on the page:

      Screenshot 2025-06-06 at 1 09 06 PM

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced an interactive, draggable actions bar within the Traffic Boost preview iframe, allowing users to accept, update, remove, or discard suggested links directly in the preview.
    • Added dynamic color customization for vertical dividers in the UI.
    • Enhanced text selection handling to better detect word boundaries and validate multi-range selections.
    • Added drag-and-drop support for UI elements within the iframe for improved interactivity.
    • Added automatic resize detection to keep the actions bar correctly positioned.
    • Added centralized injection of WordPress component styles into iframes for consistent styling.
  • Improvements

    • The actions bar remains properly positioned when resizing the iframe for a smoother user experience.
    • The preview footer now focuses solely on navigation, with link actions moved to the new actions bar.
    • Improved style injection and text sanitization for selected content.
    • Consolidated link action handlers into the iframe component for streamlined event management.
    • Enhanced highlight styling and dynamic positioning of interactive elements inside the iframe.
    • Refined highlight DOM structure and cleanup for better stability.
    • Updated comment headers and CSS class names for improved clarity and maintainability.
  • Bug Fixes

    • Prevented duplicate style injection in the iframe to ensure consistent appearance.
    • Fixed issues with multi-range text selections spanning multiple paragraphs.
  • Chores

    • Removed unnecessary blank lines and unused props for cleaner code.
    • Improved inline script injection for dashboard page initialization.

…ed. Account for existing transformations when redragging.
@alecgeatches alecgeatches self-assigned this Jun 6, 2025
@alecgeatches alecgeatches requested a review from a team as a code owner June 6, 2025 18:35
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 6, 2025

📝 Walkthrough
## Walkthrough

This change introduces a draggable, React-rendered floating actions bar inside the Traffic Boost preview iframe, enabling contextual link actions directly within the preview. The actions bar logic is moved from the footer to the iframe, with new hooks for drag-and-drop, resize handling, and dynamic positioning. Related props and UI are refactored accordingly.

## Changes

| File(s)                                                                                                         | Change Summary |
|-----------------------------------------------------------------------------------------------------------------|----------------|
| src/content-helper/common/components/vertical-divider/component.tsx                                              | Added optional `color` prop to `VerticalDivider`; fill color now customizable. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx                     | Introduced new `PreviewActions` React component for in-iframe floating action bar. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-footer.tsx                      | Removed all link action logic and props; now only renders navigation buttons. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-header.tsx<br>src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.scss | Renamed CSS class for preview header actions. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-iframe.tsx                      | Integrated `PreviewActions` into iframe, added resize handling, new action callbacks, and dynamic bar positioning. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx               | Improved style injection idempotency, word boundary detection, multi-range selection validation, and text sanitization. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts                             | Added new `useDraggable` hook for drag-and-drop within iframe. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts                      | Enhanced to render React actions bar in highlight, added dynamic popover positioning, and new DOM structure. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-resize.ts                                | Added new `useResize` hook for monitoring element resize events. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.tsx                                        | Moved link action handlers from footer to iframe; refactored props and callbacks. |
| src/content-helper/editor-sidebar/smart-linking/utils.ts                                                         | Removed trailing blank line (no functional change). |
| src/@types/assets/window.d.ts                                                                                     | Added and reordered Parsely-related properties on the global `Window` interface. |
| src/UI/class-dashboard-page.php                                                                                   | Refactored inline script injection to consolidate multiple statements. |
| src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-wordpress-component-styles.ts             | Added `useWordpressComponentStyles` hook for injecting WP component styles into iframes. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant PreviewIframe
    participant useIframeHighlight
    participant PreviewActions
    participant useDraggable

    User->>PreviewIframe: Interacts with link in iframe
    PreviewIframe->>useIframeHighlight: Highlight link, render actions bar
    useIframeHighlight->>PreviewActions: Render floating actions bar (React)
    PreviewActions-->>User: Display action buttons (Accept, Remove, etc.)
    User->>PreviewActions: Clicks action button or drags bar
    PreviewActions->>PreviewIframe: Trigger callback (accept, remove, update, etc.)
    PreviewActions->>useDraggable: Initiate drag events
    useDraggable-->>PreviewActions: Update position within iframe boundaries
    PreviewIframe->>useIframeHighlight: On resize, adjust actions bar position

Possibly related PRs

Suggested reviewers

  • acicovic

</details>

<!-- walkthrough_end -->
<!-- This is an auto-generated comment: all tool run failures by coderabbit.ai -->

> [!WARNING]
> There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.
> 
> <details>
> <summary>🔧 ESLint</summary>
> 
> > If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
> 
> npm error Exit handler never called!
> npm error This is an error with npm itself. Please report this error at:
> npm error   <https://github.com/npm/cli/issues>
> npm error A complete log of this run can be found in: /.npm/_logs/2025-06-11T15_57_02_681Z-debug-0.log
> 
> 
> 
> </details>

<!-- end of auto-generated comment: all tool run failures by coderabbit.ai -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between bced923967a3c86d0138b0cec29a0823f7fa4141 and 5c4e7807cb64df661042be8c2557d34be64039bc.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: build
* GitHub Check: E2E against WordPress latest
* GitHub Check: build

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Parsely/wp-parsely&utm_content=3445):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (2)

431-445: ⚠️ Potential issue

Unmount React roots before removing DOM elements.

The removeAndClean function should unmount any React roots before removing the container to prevent memory leaks.

Add this before removing the container:

 			const removeAndClean = ( highlight: Element, container: ParentNode, rootParent: ParentNode ) => {
+				// Unmount React root if it exists
+				const actionsContainer = container.querySelector( '.parsely-traffic-boost-popover-actions' ) as any;
+				if ( actionsContainer?._reactRoot ) {
+					actionsContainer._reactRoot.unmount();
+				}
+
 				// Create a document fragment to temporarily hold the children.
 				const fragment = iframeDocument.createDocumentFragment();

395-396: ⚠️ Potential issue

Add cleanup for React roots to prevent memory leaks.

React roots created with createRoot must be unmounted before removing DOM elements to prevent memory leaks. This issue was mentioned in previous reviews but hasn't been addressed.

Store the root reference on the container element so it can be unmounted during cleanup.

 				// Create popover content.
 				const root = createRoot( actionsContainer );
 				root.render( actionsBar );
+				// Store root reference for cleanup
+				( actionsContainer as any )._reactRoot = root;
🧹 Nitpick comments (1)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-wordpress-component-styles.ts (1)

18-18: Add period to line comment.

Line comments should end with a period according to WordPress coding standards.

-			console.error( 'WordPress component styles URL not found' ); // eslint-disable-line no-console
+			console.error( 'WordPress component styles URL not found' ); // eslint-disable-line no-console.
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5d40e9 and dd3f7d4.

⛔ Files ignored due to path filters (2)
  • build/content-helper/dashboard-page.asset.php is excluded by !build/**
  • build/content-helper/dashboard-page.js is excluded by !build/**
📒 Files selected for processing (5)
  • src/@types/assets/window.d.ts (1 hunks)
  • src/UI/class-dashboard-page.php (1 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx (5 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (14 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-wordpress-component-styles.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/UI/class-dashboard-page.php
  • src/@types/assets/window.d.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{js,ts,tsx,jsx}`: "Perform a detailed review of the provided code with following key aspects in mind: - Review the code to ensure it is well-structured and adheres to best ...

**/*.{js,ts,tsx,jsx}: "Perform a detailed review of the provided code with following key aspects in mind:

  • Review the code to ensure it is well-structured and adheres to best practices.
  • Verify compliance with WordPress coding standards.
  • Ensure the code is well-documented.
  • Check for security vulnerabilities and confirm the code is secure.
  • Optimize the code for performance, removing any unnecessary elements.
  • Validate JSDoc comments for accuracy, currency, and adherence to WordPress coding standards.
  • Ensure each line comment concludes with a period.
  • Confirm every JSDoc comment includes a @SInCE tag indicating the next version of the plugin to include the code.
  • Guarantee compatibility with the latest version of WordPress, avoiding deprecated functions or features."
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-wordpress-component-styles.ts
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: PHP 8.3
  • GitHub Check: PHP 8.1
  • GitHub Check: PHP 8.1
  • GitHub Check: PHP 8.3
  • GitHub Check: Analyze (javascript)
  • GitHub Check: build
  • GitHub Check: E2E against WordPress latest
  • GitHub Check: PHP 8.1
  • GitHub Check: Basic CS and QA checks
  • GitHub Check: build
🔇 Additional comments (8)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-wordpress-component-styles.ts (1)

13-37: Well-implemented centralization of WordPress component styles injection!

This hook successfully addresses the previous concern about hardcoded paths by retrieving the URL from the global window.wpParselyDependencies object. The implementation includes proper error handling and prevents duplicate style injection.

src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx (4)

10-10: Good refactoring to use the centralized style injection hook!

The migration to useWordpressComponentStyles properly centralizes the WordPress component style injection logic.

Also applies to: 20-24


283-283: Improved word boundary detection for contractions!

Adding the right single quotation mark (') to the regex patterns properly handles contractions and possessives.

Also applies to: 289-289


417-430: Excellent validation for multi-range selections!

The new validation logic properly ensures that multi-range selections remain within the same paragraph or list item, preventing invalid cross-paragraph selections.


481-499: Robust text extraction from complex selections!

The implementation properly extracts plain text content from multi-range selections, stripping embedded HTML and handling newlines correctly.

src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (3)

50-51: Good integration of centralized style injection!

The migration to use useWordpressComponentStyles properly centralizes the WordPress component style injection.

Also applies to: 65-65


69-232: Well-structured highlight container and action bar implementation!

The new container structure with nested highlight span and actions div provides good separation. The comprehensive styles handle positioning, alignment variants, drag handles, and responsive behavior effectively.

Also applies to: 349-351, 366-367, 386-387, 392-393


737-768: Smart positioning adjustment for action bar!

The adjustActionsBarPosition method effectively handles dynamic repositioning during resize events, preventing the action bar from overflowing iframe boundaries.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (2)

59-59: Excellent resolution of hardcoded CSS path issue.

The integration with useWordpressComponentStyles properly addresses the previous concern about hardcoded WordPress component CSS paths.

Also applies to: 74-74


353-361: Proper React root lifecycle management implemented.

The React root creation and cleanup logic correctly addresses the previous memory leak concerns by properly unmounting roots before removal and tracking them globally for cleanup.

Also applies to: 408-412

🧹 Nitpick comments (1)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (1)

78-242: Well-structured CSS with minor documentation suggestion.

The comprehensive CSS styling is well-organized and handles positioning, alignment, and visual states properly. Consider documenting the z-index choice (1000) to explain overlay hierarchy decisions.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd3f7d4 and fa35a74.

⛔ Files ignored due to path filters (2)
  • build/content-helper/dashboard-page.asset.php is excluded by !build/**
  • build/content-helper/dashboard-page.js is excluded by !build/**
📒 Files selected for processing (2)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx (6 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (13 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{js,ts,tsx,jsx}`: "Perform a detailed review of the provided code with following key aspects in mind: - Review the code to ensure it is well-structured and adheres to best ...

**/*.{js,ts,tsx,jsx}: "Perform a detailed review of the provided code with following key aspects in mind:

  • Review the code to ensure it is well-structured and adheres to best practices.
  • Verify compliance with WordPress coding standards.
  • Ensure the code is well-documented.
  • Check for security vulnerabilities and confirm the code is secure.
  • Optimize the code for performance, removing any unnecessary elements.
  • Validate JSDoc comments for accuracy, currency, and adherence to WordPress coding standards.
  • Ensure each line comment concludes with a period.
  • Confirm every JSDoc comment includes a @SInCE tag indicating the next version of the plugin to include the code.
  • Guarantee compatibility with the latest version of WordPress, avoiding deprecated functions or features."
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build
  • GitHub Check: E2E against WordPress latest
  • GitHub Check: build
🔇 Additional comments (4)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (4)

18-24: LGTM! Proper React root lifecycle management.

The global interface extension provides a clean way to track React roots for proper cleanup, addressing the previous memory leak concerns.


38-38: Well-typed React prop addition.

The actionsBar prop is properly typed as React.ReactNode and follows WordPress documentation standards.


363-365: Well-designed container structure for floating actions.

The new highlight container structure properly separates content from actions bar and supports the floating actions functionality while maintaining clean DOM organization.

Also applies to: 380-380, 392-392, 400-400, 406-406


752-783: Solid dynamic positioning implementation.

The adjustActionsBarPosition function properly handles responsive positioning with appropriate bounds checking and uses standard layout completion patterns.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx (2)

147-199: Remove unnecessary Fragment wrapper.

The Fragment around the ternary operator is redundant since it only contains one child element.

Apply this diff to remove the unnecessary Fragment:

-						{ isInboundLink && (
-							<>
-								{ selectedText ? (
+						{ isInboundLink && selectedText ? (
									<>
										<Button
											variant="primary"
											onClick={ () => onUpdateLink( activeLink, restoreOriginal ) }
											isBusy={ isAccepting }
											disabled={ isAccepting }
											icon={ isAccepting ? null : check }
										>{ isAccepting ? __( 'Updating…', 'wp-parsely' ) : __( 'Update Link', 'wp-parsely' ) }</Button>
										{ activeLink.smartLink?.is_link_replacement && (
											<CheckboxControl
												__nextHasNoMarginBottom
												label={ __( 'Restore original link?', 'wp-parsely' ) }
												checked={ restoreOriginal }
												onChange={ ( value ) => {
													setRestoreOriginal( value );
												} }
											/>
										) }
										<VerticalDivider size={ 48 } color="#1e1e1e" />
										<Button
											variant="tertiary"
											onClick={ onRestoreOriginal }
											icon={ undo }
										>
											{ __( 'Clear changes', 'wp-parsely' ) }
										</Button>
-								) : (
+									</>
+						) }
+						{ isInboundLink && ! selectedText && (
									<>
										<Button
											variant={ isRemoving ? 'primary' : 'tertiary' }
											icon={ isRemoving ? null : close }
											onClick={ () => onRemove( activeLink, restoreOriginal ) }
											isBusy={ isRemoving }
											disabled={ isRemoving }
											isDestructive
										>{ isRemoving ? __( 'Removing…', 'wp-parsely' ) : __( 'Remove Link', 'wp-parsely' ) }</Button>
										{ activeLink.smartLink?.is_link_replacement && (
											<CheckboxControl
												__nextHasNoMarginBottom
												label={ __( 'Restore original link?', 'wp-parsely' ) }
												checked={ restoreOriginal }
												onChange={ ( value ) => {
													setRestoreOriginal( value );
												} }
											/>
										) }
-								) }
-							</>
-						) }
+									</>
+						) }
🧰 Tools
🪛 Biome (1.9.4)

[error] 147-199: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)


133-133: Consider using CSS variables for colors.

The hardcoded color values could be moved to CSS variables for better maintainability and theming support.

Example approach:

-<VerticalDivider size={ 48 } color="#1e1e1e" />
+<VerticalDivider size={ 48 } color="var(--wp-parsely-text-primary)" />

Also applies to: 167-167

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa35a74 and 10689d6.

📒 Files selected for processing (10)
  • src/content-helper/common/components/loading/component.tsx (1 hunks)
  • src/content-helper/common/icons/error-icon.tsx (1 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/link-options-panel.tsx (2 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx (1 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-footer.tsx (1 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-header.tsx (2 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-iframe.tsx (9 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx (6 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts (1 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-resize.ts (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • src/content-helper/common/components/loading/component.tsx
  • src/content-helper/common/icons/error-icon.tsx
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/link-options-panel.tsx
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-header.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/text-selection-tooltip.tsx
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-footer.tsx
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-resize.ts
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-iframe.tsx
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{js,ts,tsx,jsx}`: "Perform a detailed review of the provided code with following key aspects in mind: - Review the code to ensure it is well-structured and adheres to best ...

**/*.{js,ts,tsx,jsx}: "Perform a detailed review of the provided code with following key aspects in mind:

  • Review the code to ensure it is well-structured and adheres to best practices.
  • Verify compliance with WordPress coding standards.
  • Ensure the code is well-documented.
  • Check for security vulnerabilities and confirm the code is secure.
  • Optimize the code for performance, removing any unnecessary elements.
  • Validate JSDoc comments for accuracy, currency, and adherence to WordPress coding standards.
  • Ensure each line comment concludes with a period.
  • Confirm every JSDoc comment includes a @SInCE tag indicating the next version of the plugin to include the code.
  • Guarantee compatibility with the latest version of WordPress, avoiding deprecated functions or features."
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx
🧬 Code Graph Analysis (1)
src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx (5)
src/content-helper/dashboard-page/pages/traffic-boost/provider.ts (1)
  • TrafficBoostLink (40-45)
src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.tsx (1)
  • TextSelection (28-31)
src/content-helper/dashboard-page/pages/traffic-boost/store.ts (3)
  • isGenerating (818-820)
  • isAccepting (809-811)
  • isRemoving (812-814)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts (3)
  • OnDragProps (17-21)
  • DRAG_MARGIN_PX (6-6)
  • useDraggable (29-150)
src/content-helper/common/components/vertical-divider/component.tsx (1)
  • VerticalDivider (28-34)
🪛 Biome (1.9.4)
src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx

[error] 147-199: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: E2E against WordPress latest
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (3)
src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx (3)

1-18: LGTM! Imports follow WordPress coding standards.

The imports are well-organized and follow WordPress conventions properly.


19-33: LGTM! Interface follows WordPress JSDoc standards.

The interface is well-documented with proper @SInCE versioning and clear prop definitions.


43-101: LGTM! Component logic is well-implemented.

The dragging logic correctly implements boundary constraints and follows React best practices with proper hook usage.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx (1)

152-204: Remove unnecessary Fragment wrapper.

The outer Fragment is redundant since it's the only child of the conditional expression.

Apply this diff to remove the unnecessary Fragment:

 						{ isInboundLink && (
-							<>
 								{ selectedText ? (
 									<>
 										<Button
 											variant="primary"
 											onClick={ () => onUpdateLink( activeLink, restoreOriginal ) }
 											isBusy={ isAccepting }
 											disabled={ isAccepting }
 											icon={ isAccepting ? null : check }
 										>{ isAccepting ? __( 'Updating…', 'wp-parsely' ) : __( 'Update Link', 'wp-parsely' ) }</Button>
 										{ activeLink.smartLink?.is_link_replacement && (
 											<CheckboxControl
 												__nextHasNoMarginBottom
 												label={ __( 'Restore original link?', 'wp-parsely' ) }
 												checked={ restoreOriginal }
 												onChange={ ( value ) => {
 													setRestoreOriginal( value );
 												} }
 											/>
 										) }
 										<VerticalDivider size={ 48 } color="#1e1e1e" />
 										<Button
 											variant="tertiary"
 											onClick={ onRestoreOriginal }
 											icon={ undo }
 										>
 											{ __( 'Clear changes', 'wp-parsely' ) }
 										</Button>
 									</>
 								) : (
 									<>
 										<Button
 											variant={ isRemoving ? 'primary' : 'tertiary' }
 											icon={ isRemoving ? null : close }
 											onClick={ () => onRemove( activeLink, restoreOriginal ) }
 											isBusy={ isRemoving }
 											disabled={ isRemoving }
 											isDestructive
 										>{ isRemoving ? __( 'Removing…', 'wp-parsely' ) : __( 'Remove Link', 'wp-parsely' ) }</Button>
 										{ activeLink.smartLink?.is_link_replacement && (
 											<CheckboxControl
 												__nextHasNoMarginBottom
 												label={ __( 'Restore original link?', 'wp-parsely' ) }
 												checked={ restoreOriginal }
 												onChange={ ( value ) => {
 													setRestoreOriginal( value );
 												} }
 											/>
 										) }
 									</>
 								) }
-							</>
 						) }
🧰 Tools
🪛 Biome (1.9.4)

[error] 152-204: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63600c3 and 7727d62.

⛔ Files ignored due to path filters (2)
  • build/content-helper/dashboard-page.asset.php is excluded by !build/**
  • build/content-helper/dashboard-page.js is excluded by !build/**
📒 Files selected for processing (5)
  • src/UI/class-dashboard-page.php (1 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx (1 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-iframe.tsx (8 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts (1 hunks)
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (13 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/UI/class-dashboard-page.php
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-iframe.tsx
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{js,ts,tsx,jsx}`: "Perform a detailed review of the provided code with following key aspects in mind: - Review the code to ensure it is well-structured and adheres to best ...

**/*.{js,ts,tsx,jsx}: "Perform a detailed review of the provided code with following key aspects in mind:

  • Review the code to ensure it is well-structured and adheres to best practices.
  • Verify compliance with WordPress coding standards.
  • Ensure the code is well-documented.
  • Check for security vulnerabilities and confirm the code is secure.
  • Optimize the code for performance, removing any unnecessary elements.
  • Validate JSDoc comments for accuracy, currency, and adherence to WordPress coding standards.
  • Ensure each line comment concludes with a period.
  • Confirm every JSDoc comment includes a @SInCE tag indicating the next version of the plugin to include the code.
  • Guarantee compatibility with the latest version of WordPress, avoiding deprecated functions or features."
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts
  • src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx
🧬 Code Graph Analysis (2)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (2)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-wordpress-component-styles.ts (1)
  • useWordpressComponentStyles (13-37)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts (1)
  • DRAG_MARGIN_PX (6-6)
src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx (5)
src/content-helper/dashboard-page/pages/traffic-boost/provider.ts (1)
  • TrafficBoostLink (40-45)
src/content-helper/dashboard-page/pages/traffic-boost/preview/preview.tsx (1)
  • TextSelection (28-31)
src/content-helper/dashboard-page/pages/traffic-boost/store.ts (3)
  • isGenerating (818-820)
  • isAccepting (809-811)
  • isRemoving (812-814)
src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-draggable.ts (3)
  • OnDragProps (17-21)
  • DRAG_MARGIN_PX (6-6)
  • useDraggable (29-153)
src/content-helper/common/components/vertical-divider/component.tsx (1)
  • VerticalDivider (28-34)
🪛 Biome (1.9.4)
src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx

[error] 152-204: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build
  • GitHub Check: E2E against WordPress latest
  • GitHub Check: build
🔇 Additional comments (6)
src/content-helper/dashboard-page/pages/traffic-boost/preview/components/preview-actions.tsx (1)

1-210: Excellent component implementation!

The PreviewActions component is well-structured with proper WordPress coding standards compliance:

  • Clean separation of concerns with clear prop interfaces
  • Proper JSDoc documentation with @SInCE tags
  • Appropriate use of WordPress data selectors and components
  • Good accessibility support (drag handle has proper ARIA attributes)
  • Logical conditional rendering based on link types and selection state
  • Proper integration with the useDraggable hook for positioning constraints

The drag functionality and button states are handled correctly, providing a smooth user experience.

🧰 Tools
🪛 Biome (1.9.4)

[error] 152-204: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/content-helper/dashboard-page/pages/traffic-boost/preview/hooks/use-iframe-highlight.ts (5)

18-24: Well-designed global state management.

The global window interface declaration for tracking the React root is a clean solution for managing the single floating actions bar instance within the iframe context. This approach properly encapsulates the implementation detail while enabling necessary cleanup.


78-241: Comprehensive CSS styling for floating actions bar.

The CSS styles cover all necessary scenarios:

  • Proper positioning with responsive alignment classes
  • Clean visual design with appropriate spacing and borders
  • Drag handle styling with proper cursor states
  • Button layout and sizing
  • Responsive behavior with left/right alignment options

The styles integrate well with WordPress component styles and provide a polished user interface.


353-412: Excellent React root management implementation.

The React rendering logic properly handles:

  • Cleanup of existing actions before creating new ones
  • Proper unmounting of React roots to prevent memory leaks
  • Clean DOM structure with highlight containers
  • Integration of the actions bar into the highlight flow

The implementation follows React best practices for portal-like rendering within iframes.


751-782: Smart responsive positioning logic.

The adjustActionsBarPosition method provides intelligent positioning:

  • Removes existing alignment classes before recalculating
  • Uses proper timing with setTimeout to allow layout completion
  • Applies margin-aware boundary checking with DRAG_MARGIN_PX
  • Handles both left and right overflow scenarios

This ensures the actions bar remains visible and usable across different viewport sizes.


1-792: Outstanding integration of React components with iframe highlighting.

The enhanced useIframeHighlight hook successfully integrates a React-based actions bar into the iframe highlighting system while maintaining:

  • Clean separation of concerns
  • Proper memory management with React root cleanup
  • Responsive positioning logic
  • Enhanced DOM structure for better containment
  • Improved error handling and logging

The implementation demonstrates excellent technical execution and follows WordPress coding standards throughout.

@alecgeatches
Copy link
Contributor Author

@acicovic Thank you for your comments and commits to fix things up. This should be ready for another review, thank you!

Copy link
Collaborator

@acicovic acicovic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good to go! Thank you for your work on this, as well as for providing such an excellent, thorough and clear PR description.

@alecgeatches alecgeatches merged commit d635a4a into develop Jun 11, 2025
33 checks passed
@alecgeatches alecgeatches deleted the add/floating-boost-link-actions branch June 11, 2025 16:01
github-actions bot added a commit that referenced this pull request Jun 11, 2025
…actions Add floating Traffic Boost actions bar" (d635a4a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Changelog: Changed PR to be added under the changelog's "Changed" section Feature: Engagement Boost Ticket/PR related to Engagement Boost Feature: PCI Ticket/PR related to Content Intelligence

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants