Skip to content

feat: onboarding tour with name changes#3

Open
Arukuen wants to merge 6 commits into
developfrom
feat-onboarding-tour
Open

feat: onboarding tour with name changes#3
Arukuen wants to merge 6 commits into
developfrom
feat-onboarding-tour

Conversation

@Arukuen
Copy link
Copy Markdown
Contributor

@Arukuen Arukuen commented Oct 22, 2025

Summary by CodeRabbit

  • New Features

    • Guided modal tours added across editor, sidebar, and interaction library with multi-step walkthroughs, target highlighting, navigation, CTA support, and persisted completion state (client + admin endpoints).
  • Documentation

    • New guides covering tour creation, configuration, discovery, and step structure.
  • UX

    • Applying presets/configurations is deferred so processing finishes before closing modals and opening the sidebar; timeline preview/button styling updated.
  • Style

    • Comprehensive tour modal styles, overlays, glow animations, and added CSS hooks for targeted UI styling.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 22, 2025

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cdbf5d68-87c7-438d-b237-92bc40d2c27f

📥 Commits

Reviewing files that changed from the base of the PR and between 385457f and ebdae9b.

📒 Files selected for processing (5)
  • src/editor/components/guided-modal-tour/editor.scss
  • src/editor/components/guided-modal-tour/index.js
  • src/editor/components/modal-tour/index.js
  • src/editor/getting-started.php
  • src/editor/interaction-library/configure-modal.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/editor/interaction-library/configure-modal.js
  • src/editor/components/modal-tour/index.js
  • src/editor/components/guided-modal-tour/editor.scss

📝 Walkthrough

Walkthrough

Adds a guided modal tour system (components, modal runtime, tours, styles), PHP persistence/localization for tour state, integrates GuidedModalTour into editor surfaces (editor root, sidebar, interaction library), defers configure-modal apply work into an async callback, and applies small UI classname tweaks.

Changes

Guided Modal Tour

Layer / File(s) Summary
PHP bootstrap & settings
interactions.php, src/editor/getting-started.php
Requires the getting-started PHP file and registers interact_guided_tour_states, exposing guidedTourStates to editor scripts and a REST endpoint to persist per-user tour state.
GuidedModalTour component & assets
src/editor/components/guided-modal-tour/..., src/editor/components/index.js
Adds the GuidedModalTour wrapper, TOUR_CONDITIONS, global tour util (active tour tracking + listeners), SCSS, and README for guided-tour authoring; exports GuidedModalTour from components index.
Modal runtime, steps, and tours
src/editor/components/modal-tour/..., src/editor/components/modal-tour/tours/*
Implements ModalTour runtime (positioning, glow, event-driven advancement, confetti, Steps indicator), aggregates TOUR_STEPS, and adds multiple tour definitions plus tours README and auto-discovery.
Editor & Interaction Library integration
src/editor/editor.js, src/editor/components/interaction-panel/..., src/editor/interaction-library/...
Imports and renders GuidedModalTour with tourId="editor", tourId="sidebar", and tourId="interaction-library" in respective editor surfaces and modal content; includes small UI class/name tweaks for styling hooks.
Async configure apply flow
src/editor/interaction-library/configure-modal.js
Defers handleApply work into setTimeout: run preset/config, apply mappings, rebuild interactions/actions, dispatch add/save events, then close modal and open sidebar.
Minor UI hooks & class tweaks
src/editor/components/timeline/index.js, src/editor/interaction-library/select-modal.js, src/editor/plugins/*
Adds className hooks and renames toolbar button classes and insertion selector to support styling and deterministic placement.

Sequence Diagram(s)

sequenceDiagram
    participant Editor as Editor UI
    participant GMT as GuidedModalTour
    participant Util as Tour State Manager
    participant MT as ModalTour
    participant PHP as Interact_Getting_Started_Screen

    Editor->>GMT: Render GuidedModalTour(tourId)
    GMT->>Util: isTourActive() / getActiveTourId()
    alt Active tour exists
        GMT-->>Editor: Do not mount ModalTour
    else No active tour
        GMT->>Util: addTourStateListener(...)
        GMT->>PHP: read localized guidedTourStates
        alt Tour should show
            GMT->>MT: Mount ModalTour (lazy)
            GMT->>Util: setActiveTour(tourId)
            MT->>MT: initialize, compute position, show step
            loop User interaction
                MT->>MT: preStep/postStep, handle next/back, nextEvent
                MT->>MT: update step, reposition, show glow
            end
            MT->>GMT: onClose
            GMT->>Util: clearActiveTour()
            GMT->>PHP: update guidedTourStates (append tourId)
            GMT->>Editor: remove tour query param
        else Skip render
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through code to plant a guide,

Glows and steps where hints can hide,
States we save and steps we show,
A tiny rabbit says, "Let's go!" 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary changes: introducing a new onboarding/guided modal tour feature with CSS class name updates throughout the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-onboarding-tour

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Arukuen Arukuen self-assigned this Oct 22, 2025
Copy link
Copy Markdown
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: 8

🧹 Nitpick comments (13)
src/editor/getting-started.php (2)

14-16: Fix indentation consistency.

The indentation appears to use spaces instead of tabs, which is inconsistent with WordPress PHP coding standards. Ensure consistent use of tabs for indentation throughout the file.

Apply this diff to fix the indentation:

-            // Register settings.
-            add_action( 'admin_init', array( $this, 'register_settings' ) );
-            add_action( 'rest_api_init', array( $this, 'register_settings' ) );
+		// Register settings.
+		add_action( 'admin_init', array( $this, 'register_settings' ) );
+		add_action( 'rest_api_init', array( $this, 'register_settings' ) );

32-32: Clarify description text.

The description mentions "block tours" but this setting is for guided interaction tours. Consider updating the text to accurately reflect its purpose.

Apply this diff:

-'description' => __( 'An array of strings representing completed block tours.', 'interactions' ),
+'description' => __( 'An array of strings representing completed guided tours.', 'interactions' ),
src/editor/components/modal-tour/tours/index.js (2)

22-30: Consider making the export pattern more explicit.

The fallback logic that takes the first named export (line 28) could be unpredictable if a tour file accidentally exports multiple named items. Consider either:

  1. Documenting that tour files should always use default exports OR a single named export matching the filename
  2. Adding a warning when multiple named exports are found
  3. Being more specific about which named export to use (e.g., matching the filename)

Consider adding validation:

 	// Prefer default export, fallback to first named export if available
 	if ( tourModule.default ) {
 		tours[ tourName ] = tourModule.default
 	} else {
 		// If no default export, use the first named export (if any)
 		const namedExports = Object.keys( tourModule ).filter( name => name !== 'default' )
 		if ( namedExports.length > 0 ) {
+			if ( namedExports.length > 1 ) {
+				console.warn( `Tour file ${ fileName } has multiple named exports. Using the first one: ${ namedExports[ 0 ] }` )
+			}
 			tours[ tourName ] = tourModule[ namedExports[ 0 ] ]
 		}
 	}

5-5: Consider adding file extension filter for clarity.

The regex /\.js$/ will match any .js file, but for better maintainability and to avoid accidentally picking up other JavaScript files (like test files if added later), consider being more explicit or adding a naming convention check.

You could document that tour files should follow a specific naming pattern, or update the pattern to be more restrictive:

// Only match files that look like tour definitions (no .test.js, .spec.js, etc.)
const tourContext = require.context( './', false, /^(?!.*\.(test|spec)).*\.js$/ )
src/editor/components/guided-modal-tour/editor.scss (2)

64-64: Remove or uncomment the commented border style.

The commented-out border style should either be removed if not needed or uncommented if it serves a purpose during development.

 		border-radius: 16px;
-		// border: 1px solid #f00069ad;
 	}

178-190: Clean up commented styles or document their purpose.

The step indicator styling includes commented-out cursor and hover styles. If these are intentionally disabled, consider adding a comment explaining why. Otherwise, remove them to keep the code clean.

 	height: 8px;
 	border-radius: 20px;
 	background-color: #e1e1e1;
-	// cursor: pointer;
 	padding: 0 !important;
 	margin: 0 !important;

 	&--active {
 		background: #05f;
 		width: 24px;
 		border-radius: 20px;
 	}
-
-	// &:hover {
-	//     background-color: #aaa;
-	// }
 }
src/editor/components/guided-modal-tour/index.js (1)

62-63: Add defensive error handling for condition evaluation.

The condition function is called without error handling. If a tour condition throws an exception, it will break the entire component.

Add a try-catch wrapper:

 	const condition = TOUR_CONDITIONS[ tourId ]
-	const conditionResult = condition ? condition() : null
+	let conditionResult = null
+	if ( condition ) {
+		try {
+			conditionResult = condition()
+		} catch ( error ) {
+			console.error( `Error evaluating condition for tour "${ tourId }":`, error )
+			conditionResult = null // Fallback to default behavior
+		}
+	}
src/editor/components/modal-tour/tours/editor.js (1)

13-17: Stabilize and de‑duplicate the button selector

The same selector is repeated 3x. Centralize it to avoid drift and consider moving to a stable data-* hook to survive class renames.

Apply this diff:

 import { __ } from '@wordpress/i18n'
 import { createInterpolateElement } from '@wordpress/element'

+// Prefer a stable data-* selector in markup; fallback to class until available.
+const INSERT_BTN_SEL = '.interact-insert-library-button'
+
 export const editor = {
   hasConfetti: false,
   steps: [
     {
       title: '👋 ' + __( 'Welcome to Interactions', 'interactions' ),
       description: __( 'Transform your WordPress site with animations and dynamic interactions that bring your content to life. Let’s get started by exploring the Interaction Library.', 'interactions' ),
       help: createInterpolateElement( __( 'Click the <strong>Interactions</strong> button to continue.', 'interactions' ), {
         strong: <strong />,
       } ),
-      anchor: '.interact-insert-library-button',
+      anchor: INSERT_BTN_SEL,
       position: 'bottom',
-      nextEventTarget: '.interact-insert-library-button',
-      glowTarget: '.interact-insert-library-button',
+      nextEventTarget: INSERT_BTN_SEL,
+      glowTarget: INSERT_BTN_SEL,
       showNext: false,
     },
   ],
 }
src/editor/components/guided-modal-tour/util.js (1)

17-20: Avoid redundant notifications and validate listeners

Minor polish: skip notifying when the value is unchanged, and guard listener type.

Apply this diff:

 export const setActiveTour = tourId => {
-  activeTourId = tourId
-  tourStateListeners.forEach( listener => listener( tourId ) )
+  if ( activeTourId === tourId ) return
+  activeTourId = tourId
+  tourStateListeners.forEach( listener => listener( tourId ) )
 }
 
 export const clearActiveTour = () => {
-  activeTourId = null
-  tourStateListeners.forEach( listener => listener( null ) )
+  if ( activeTourId === null ) return
+  activeTourId = null
+  tourStateListeners.forEach( listener => listener( null ) )
 }
 
 export const addTourStateListener = listener => {
-  tourStateListeners.add( listener )
+  if ( typeof listener !== 'function' ) {
+    // No-op remove function for invalid input
+    return () => {}
+  }
+  tourStateListeners.add( listener )
   return () => tourStateListeners.delete( listener )
 }

Also applies to: 25-28, 50-53

src/editor/components/modal-tour/tours/sidebar.js (2)

26-33: Honor reduced‑motion preference when scrolling

Respect users with motion sensitivity by disabling smooth scroll when prefers-reduced-motion is set.

Apply this diff:

       preStep: () => {
-        // Scroll to the preview button before moving to the next step.
-        document.querySelector( '.interact-timeline__preview-button' )?.scrollIntoView( {
-          behavior: 'smooth',
-          block: 'center',
-        } )
+        // Scroll to the preview button before moving to the next step.
+        const btn = document.querySelector( '.interact-timeline__preview-button' )
+        if ( ! btn ) return
+        const prefersReduced =
+          typeof window !== 'undefined' &&
+          window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches
+        btn.scrollIntoView( {
+          behavior: prefersReduced ? 'auto' : 'smooth',
+          block: 'center',
+        } )
       },

47-61: Add guards around data store access

The block editor store should exist here, but add safe checks to avoid rare runtime errors in atypical contexts.

Apply this diff:

-        const blocks = select( 'core/block-editor' ).getBlocks()
+        const editorStore = select( 'core/block-editor' )
+        const blocks = editorStore?.getBlocks?.() || []
@@
-          dispatch( 'core/block-editor' ).selectBlock( innermostBlock.clientId )
+          dispatch( 'core/block-editor' )?.selectBlock?.( innermostBlock.clientId )
src/editor/components/modal-tour/index.js (2)

193-202: Include skipIf in deps and guard against exceptions

Ensure the latest skipIf is used and defensive against errors.

Apply this diff:

-useEffect( () => {
-  if ( skipIf() ) {
+useEffect( () => {
+  let shouldSkip = false
+  try {
+    shouldSkip = !!skipIf()
+  } catch (e) {
+    shouldSkip = false
+  }
+  if ( shouldSkip ) {
     if ( direction === 'forward' ) {
       handleNextEvent()
     } else {
       handleBackEvent()
     }
   }
-}, [ currentStep, direction ] )
+}, [ currentStep, direction, skipIf, handleNextEvent, handleBackEvent ] )

342-345: Correct glow size thresholds (medium branch unreachable for width)

The second condition repeats width > 300, making the width path for medium unreachable. Use a lower width threshold.

Apply this diff:

-    const glowTargetSize = targetRect.width > 300 || targetRect.height > 200 ? 'large'
-      : targetRect.width > 300 || targetRect.height > 100 ? 'medium'
+    const glowTargetSize = targetRect.width > 300 || targetRect.height > 200 ? 'large'
+      : targetRect.width > 200 || targetRect.height > 100 ? 'medium'
       : 'small'
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6e34f9 and a97f1dd.

📒 Files selected for processing (23)
  • interactions.php (1 hunks)
  • src/editor/components/guided-modal-tour/README.MD (1 hunks)
  • src/editor/components/guided-modal-tour/editor.scss (1 hunks)
  • src/editor/components/guided-modal-tour/index.js (1 hunks)
  • src/editor/components/guided-modal-tour/tour-conditions.js (1 hunks)
  • src/editor/components/guided-modal-tour/util.js (1 hunks)
  • src/editor/components/index.js (1 hunks)
  • src/editor/components/interaction-panel/index.js (2 hunks)
  • src/editor/components/modal-tour/index.js (1 hunks)
  • src/editor/components/modal-tour/tour-steps.js (1 hunks)
  • src/editor/components/modal-tour/tours/README.md (1 hunks)
  • src/editor/components/modal-tour/tours/editor.js (1 hunks)
  • src/editor/components/modal-tour/tours/index.js (1 hunks)
  • src/editor/components/modal-tour/tours/interaction-library.js (1 hunks)
  • src/editor/components/modal-tour/tours/sidebar.js (1 hunks)
  • src/editor/components/timeline/index.js (1 hunks)
  • src/editor/editor.js (2 hunks)
  • src/editor/getting-started.php (1 hunks)
  • src/editor/interaction-library/index.js (3 hunks)
  • src/editor/interaction-library/select-modal.js (1 hunks)
  • src/editor/plugins/block-toolbar-button/index.js (1 hunks)
  • src/editor/plugins/top-toolbar-button/add-interaction-button.js (1 hunks)
  • src/editor/plugins/top-toolbar-button/index.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
src/editor/components/modal-tour/tour-steps.js (1)
src/editor/components/modal-tour/tours/index.js (1)
  • tours (6-6)
src/editor/editor.js (1)
src/editor/components/guided-modal-tour/index.js (1)
  • GuidedModalTour (27-104)
src/editor/components/interaction-panel/index.js (1)
src/editor/components/guided-modal-tour/index.js (1)
  • GuidedModalTour (27-104)
src/editor/interaction-library/index.js (1)
src/editor/components/guided-modal-tour/index.js (1)
  • GuidedModalTour (27-104)
src/editor/components/guided-modal-tour/index.js (2)
src/editor/components/guided-modal-tour/util.js (8)
  • isTourActive (35-35)
  • isTourActive (35-35)
  • getActiveTourId (42-42)
  • getActiveTourId (42-42)
  • addTourStateListener (50-53)
  • addTourStateListener (50-53)
  • clearActiveTour (25-28)
  • clearActiveTour (25-28)
src/editor/components/guided-modal-tour/tour-conditions.js (2)
  • TOUR_CONDITIONS (2-2)
  • TOUR_CONDITIONS (2-2)
src/editor/components/modal-tour/index.js (2)
src/editor/components/modal-tour/tour-steps.js (2)
  • TOUR_STEPS (4-6)
  • TOUR_STEPS (4-6)
src/editor/components/guided-modal-tour/util.js (6)
  • setActiveTour (17-20)
  • setActiveTour (17-20)
  • getActiveTourId (42-42)
  • getActiveTourId (42-42)
  • clearActiveTour (25-28)
  • clearActiveTour (25-28)
🪛 LanguageTool
src/editor/components/modal-tour/tours/README.md

[style] ~65-~65: To form a complete sentence, be sure to include a subject.
Context: ...tring, optional) The size of the modal. Can be: - 'small' (default) - 'medium' ...

(MISSING_IT_THERE)


[style] ~74-~74: To form a complete sentence, be sure to include a subject.
Context: ...on of the modal relative to the anchor. Can be: - 'left' - 'right' - 'top' - ...

(MISSING_IT_THERE)

🔇 Additional comments (22)
src/editor/plugins/block-toolbar-button/index.js (1)

130-130: LGTM! Clean styling hook addition.

The className addition follows the project's naming convention and enables targeted styling or selection for the guided tour feature without affecting existing functionality.

src/editor/components/guided-modal-tour/README.MD (1)

1-36: LGTM! Clear and helpful documentation.

The documentation provides a well-structured guide for creating new tours with clear steps and examples. This will help developers implement tours consistently across the codebase.

src/editor/getting-started.php (1)

23-45: LGTM! Setting registration is well-structured.

The setting registration properly exposes the array to the REST API with appropriate schema and sanitization. The default empty array is sensible.

src/editor/components/index.js (1)

11-11: LGTM! Export follows existing patterns.

The GuidedModalTour export is correctly added and follows the same pattern as other component exports in this file.

src/editor/components/interaction-panel/index.js (2)

2-2: LGTM! Import is correctly added.

The GuidedModalTour import is properly added to the existing import statement.


636-636: LGTM! Tour integration is well-placed.

The GuidedModalTour component with tourId="sidebar" is appropriately placed at the end of the InteractionPanel render, allowing it to overlay the panel content when active.

src/editor/components/modal-tour/tour-steps.js (1)

1-6: LGTM! Clean aggregation pattern.

The file provides a clean centralized export for tour steps by spreading the tours object. This pattern allows for easy extension and maintains a single source of truth.

src/editor/interaction-library/index.js (3)

12-12: LGTM! Import is correctly added.

The GuidedModalTour import is properly added and follows the existing import pattern.


197-200: Verify setTimeout doesn't cause issues on unmount.

Wrapping the state resets in a 100ms setTimeout may cause issues if the component unmounts before the timeout fires, potentially leading to React warnings about setting state on an unmounted component. Consider cleanup in a useEffect return or using a ref to track mount status.

If this delay is intentional (e.g., to allow the tour to render before state cleanup), consider adding a comment explaining the rationale and verifying that cleanup is properly handled:

useEffect(() => {
  let timeoutId;
  // Cleanup function
  return () => {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
  };
}, []);

276-276: LGTM! Tour placement is appropriate.

The GuidedModalTour component is correctly placed inside the Modal, allowing it to display the tour overlay for the interaction library.

interactions.php (1)

138-138: LGTM! File inclusion is properly placed.

The require_once for getting-started.php is correctly added with the proper path and follows the existing pattern in the file.

src/editor/components/timeline/index.js (1)

1113-1113: LGTM! CSS class added for styling/targeting.

The className="interact-timeline__preview-button" attribute provides a styling hook and likely helps the tour system target this button. The BEM naming convention is consistent with the codebase.

src/editor/components/guided-modal-tour/tour-conditions.js (1)

1-2: LGTM - Placeholder for future tour conditions.

The empty TOUR_CONDITIONS object with clear documentation is ready for future per-tour condition overrides. Note that individual tour configurations can also define their own condition functions (as documented in the README), so this serves as a centralized override mechanism when needed.

src/editor/components/modal-tour/tours/README.md (2)

1-164: Excellent documentation for the tour system.

The README provides comprehensive guidance on creating tours, including:

  • Tour structure and properties
  • Step-level configuration options
  • Complete working examples
  • Clear explanations of the discovery mechanism

This will significantly help future contributors create new tours.


3-3: No issues found—the referenced file path is correct.

The file src/editor/components/guided-modal-tour/README.MD exists exactly as referenced in the documentation with the uppercase .MD extension. The path and case are correct and require no changes.

src/editor/interaction-library/select-modal.js (1)

96-96: LGTM - Enables category-specific targeting for guided tours.

Adding per-category CSS classes allows the guided tour system to precisely target and highlight specific categories. This is a clean implementation that aligns with the tour's glowTarget functionality seen in src/editor/components/modal-tour/tours/interaction-library.js.

src/editor/components/modal-tour/tours/interaction-library.js (1)

1-51: LGTM - Well-structured interactive tour.

The interaction library tour provides a clear, step-by-step onboarding experience:

  • Proper i18n for all user-facing text
  • Interactive steps that wait for user actions
  • Appropriate use of glow targets and anchoring

The offset values use string format (e.g., '-400px'), which should work with CSS custom properties but consider verifying this matches the expected format in the ModalTour component.

src/editor/components/guided-modal-tour/editor.scss (1)

1-236: Comprehensive and well-structured styling.

The SCSS provides excellent visual polish for the guided tour system:

  • Proper z-index layering for overlay and glow effects
  • Smooth transitions and animations
  • Flexible positioning system with CSS custom properties
  • Multiple position variants for different anchoring scenarios
src/editor/components/guided-modal-tour/index.js (2)

42-47: LGTM - Proper cleanup of event listener.

The effect hook correctly subscribes to tour state changes and returns the cleanup function to remove the listener on unmount or when tourId changes.


27-30: LGTM - Using memo appropriately.

The component is wrapped in memo which is appropriate given it only depends on the tourId prop and includes internal state management.

src/editor/editor.js (2)

9-9: GuidedModalTour import is correctly resolved.

Verification confirms that GuidedModalTour is properly exported through the import chain:

  • src/editor/components/index.js line 11 re-exports it from ./guided-modal-tour
  • src/editor/components/guided-modal-tour/index.js line 106 exports it as default

The import in src/editor/editor.js resolves correctly. No issues found.


449-449: No issues found — editor tour is properly defined and integrated.

The editor tour exists at src/editor/components/modal-tour/tours/editor.js with a well-formed export structure. The dynamic discovery pattern in index.js correctly handles the named export, and the integration through tour-steps.js to the ModalTour component works as intended. The tourId="editor" prop will correctly resolve to the tour definition.

Comment thread src/editor/components/guided-modal-tour/index.js Outdated
Comment thread src/editor/components/guided-modal-tour/index.js
Comment thread src/editor/components/modal-tour/index.js
Comment thread src/editor/components/modal-tour/index.js Outdated
Comment thread src/editor/components/modal-tour/index.js
Comment thread src/editor/components/modal-tour/tours/sidebar.js Outdated
Comment thread src/editor/plugins/top-toolbar-button/add-interaction-button.js
Comment thread src/editor/plugins/top-toolbar-button/index.js
Copy link
Copy Markdown
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

♻️ Duplicate comments (1)
src/editor/components/modal-tour/index.js (1)

367-380: Duplicate: Close button handler has unstable dependencies.

This effect still includes modalRef.current in the dependency array, which is problematic because ref mutations don't trigger re-renders. The previous review comment suggested using isVisible in the dependencies instead and adding proper guards for querySelector.

For reference, the suggested fix from the previous review:

-useEffect( () => {
-  const handleHeaderClick = () => {
-    onClose()
-  }
-  if ( modalRef.current ) {
-    modalRef.current.querySelector( '.components-modal__header > .components-button' ).addEventListener( 'click', handleHeaderClick )
-  }
-  return () => {
-    if ( modalRef.current ) {
-      modalRef.current.querySelector( '.components-modal__header > .components-button' ).removeEventListener( 'click', handleHeaderClick )
-    }
-  }
-}, [ modalRef.current, onClose ] )
+useEffect( () => {
+  const handleHeaderClick = () => onClose()
+  const closeBtn = modalRef.current?.querySelector?.( '.components-modal__header > .components-button' )
+  closeBtn?.addEventListener( 'click', handleHeaderClick )
+  return () => closeBtn?.removeEventListener( 'click', handleHeaderClick )
+}, [ isVisible, onClose ] )
🧹 Nitpick comments (2)
src/editor/components/modal-tour/index.js (2)

194-203: Include callback dependencies in effect array.

The effect calls handleNextEvent and handleBackEvent but doesn't list them in the dependency array. While they're stable due to useCallback, including them follows React best practices and satisfies the exhaustive-deps rule.

Apply this diff:

 	// If we just moved to this step, even before showing it check if we should skip it, if so, move to the next/prev step.
 	useEffect( () => {
 		if ( skipIf() ) {
 			if ( direction === 'forward' ) {
 				handleNextEvent()
 			} else {
 				handleBackEvent()
 			}
 		}
-	}, [ currentStep, direction ] )
+	}, [ currentStep, direction, handleNextEvent, handleBackEvent, skipIf ] )

274-338: Remove modalRef.current from dependencies.

Including modalRef.current in the useMemo dependency array is an anti-pattern because ref mutations don't trigger re-renders and reading ref.current during render is unreliable. The forceRefresh state already handles re-computation when needed.

Apply this diff:

-	}, [ anchor, position, modalRef.current, isVisible, isVisibleDelayed, isTransitioning, forceRefresh ] )
+	}, [ anchor, position, isVisible, isVisibleDelayed, isTransitioning, forceRefresh ] )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a97f1dd and c4a6087.

📒 Files selected for processing (3)
  • src/editor/components/guided-modal-tour/index.js (1 hunks)
  • src/editor/components/modal-tour/index.js (1 hunks)
  • src/editor/components/modal-tour/tours/sidebar.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/editor/components/modal-tour/tours/sidebar.js
🧰 Additional context used
🧬 Code graph analysis (2)
src/editor/components/guided-modal-tour/index.js (3)
src/editor/components/modal-tour/index.js (2)
  • props (496-499)
  • ModalTour (33-464)
src/editor/components/guided-modal-tour/util.js (8)
  • isTourActive (35-35)
  • isTourActive (35-35)
  • getActiveTourId (42-42)
  • getActiveTourId (42-42)
  • addTourStateListener (50-53)
  • addTourStateListener (50-53)
  • clearActiveTour (25-28)
  • clearActiveTour (25-28)
src/editor/components/guided-modal-tour/tour-conditions.js (2)
  • TOUR_CONDITIONS (2-2)
  • TOUR_CONDITIONS (2-2)
src/editor/components/modal-tour/index.js (3)
src/editor/components/guided-modal-tour/index.js (2)
  • ModalTour (73-73)
  • props (28-30)
src/editor/components/modal-tour/tour-steps.js (2)
  • TOUR_STEPS (4-6)
  • TOUR_STEPS (4-6)
src/editor/components/guided-modal-tour/util.js (6)
  • setActiveTour (17-20)
  • setActiveTour (17-20)
  • getActiveTourId (42-42)
  • getActiveTourId (42-42)
  • clearActiveTour (25-28)
  • clearActiveTour (25-28)
🔇 Additional comments (11)
src/editor/components/guided-modal-tour/index.js (4)

1-24: LGTM!

The imports are well-organized and include all necessary dependencies for tour state management, conditions, and React hooks.


27-56: LGTM!

The state management and tour conflict prevention logic is well-implemented. The listener cleanup is properly handled in the effect's return function.


58-70: LGTM!

The condition logic correctly handles the three-state system (true/false/null) and appropriately gates tour visibility based on completion state.


79-102: LGTM!

The onClose handler correctly manages tour completion: clears active tour state, persists to WordPress settings with error handling, updates the global cache, and cleans up URL parameters. Previous review concerns about error handling have been addressed.

src/editor/components/modal-tour/index.js (7)

1-46: LGTM!

The imports, constant definitions, and initial component setup are well-structured. State management and ref usage are appropriate for the tour functionality.


48-78: LGTM!

The tour configuration and step access are properly guarded with fallbacks (|| {}), preventing crashes when tourId is invalid or steps are empty. Previous review concerns have been addressed.


80-111: LGTM!

The initialization and active tour management effects are well-implemented with proper cleanup. The force refresh interval ensures modal positioning stays accurate during visibility.


113-192: LGTM!

Both navigation handlers (handleNextEvent and handleBackEvent) properly manage transitions, visibility states, and step lifecycle hooks with appropriate optional chaining. The callback dependencies are correctly specified.


205-272: LGTM!

The initial visibility delay, event listener setup (with proper delegation for click events), and glow element lifecycle are all correctly implemented with appropriate cleanup functions.


340-365: LGTM!

The glow target positioning logic correctly calculates size classes and updates the glow element's position dynamically. Proper guards ensure safe DOM access.


382-522: LGTM!

The render logic, confetti animation, and Steps indicator component are all well-implemented. The dynamic styling via inline <style> and conditional rendering are appropriate for this use case.

Comment thread src/editor/components/guided-modal-tour/index.js Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

🤖 Pull request artifacts

file commit
pr3-interactions-3-merge.zip ebdae9b

github-actions Bot added a commit that referenced this pull request Jun 1, 2026
Copy link
Copy Markdown
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 (1)
src/editor/interaction-library/configure-modal.js (1)

236-245: ⚡ Quick win

Consider adding a loading state to the apply button during async processing.

Since handleApply now defers all work inside setTimeout, the button click returns immediately while the actual insertion/application happens asynchronously. Without a loading indicator or disabled state, users receive no visual feedback that work is in progress and might click the button multiple times.

💡 Suggested improvement

Add a isApplying state and disable the button during processing:

 	const [ optionValues, setOptionValues ] = useState( {} )
 	const [ selectedTarget, setSelectedTarget ] = useState( interactionTarget )
 	const [ sideBarEl, setSideBarEl ] = useState( null )
+	const [ isApplying, setIsApplying ] = useState( false )
 	const handleApply = () => {
+		setIsApplying( true )
 		setTimeout( () => {
 			try {
 			const config = getConfig( selectedPreset.config )
 			// ... rest of logic ...
 			closeModal()
 			openInteractionsSidebar()
 			} catch ( error ) {
 				console.error( 'Interactions Library apply error:', error ) // eslint-disable-line no-console
+				setIsApplying( false )
 			}
 		} )
 	}
 			<div className="interact-interaction-library__configure__apply-button">
 				<Button
 					variant="primary"
 					className="interact-interaction-library__configure__apply-button__button"
 					onClick={ handleApply }
+					isBusy={ isApplying }
+					disabled={ isApplying }
 				>
 					{ mode === 'insert'
 						? __( 'Insert Interaction', 'interactions' )
 						: __( 'Apply Interaction', 'interactions' )
 					}
 				</Button>
 			</div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/editor/interaction-library/configure-modal.js` around lines 236 - 245,
The apply button currently returns immediately because handleApply defers work
with setTimeout and provides no feedback; add a local boolean state (e.g.,
isApplying) in the ConfigureModal component, set isApplying = true at the start
of handleApply before scheduling the async work and set isApplying = false in
the final callback/cleanup (or in both success/error branches), then pass
isApplying to the Button to disable it and optionally change its label or show a
spinner (use mode and existing className to keep styling consistent); this
prevents duplicate clicks while the deferred insertion/application runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/editor/interaction-library/configure-modal.js`:
- Around line 60-156: The deferred apply logic inside the setTimeout callback
may throw after the small try/catch around config(); wrap the entire setTimeout
callback body in a try/catch that catches any error from parse(),
dispatch(...).insertBlocks, applyTargetMappings, createNewAction,
createNewInteraction, updateInteraction, etc., log the error and surface a
user-visible failure (e.g., dispatch a notice) and abort closing the
modal/opening the sidebar and abort saving the post to avoid leaving the editor
inconsistent; ensure dispatch('interact/interactions').setDidModifyPostContent
remains correct on error and only proceed to closeModal() /
openInteractionsSidebar() / savePost() on successful completion.

---

Nitpick comments:
In `@src/editor/interaction-library/configure-modal.js`:
- Around line 236-245: The apply button currently returns immediately because
handleApply defers work with setTimeout and provides no feedback; add a local
boolean state (e.g., isApplying) in the ConfigureModal component, set isApplying
= true at the start of handleApply before scheduling the async work and set
isApplying = false in the final callback/cleanup (or in both success/error
branches), then pass isApplying to the Button to disable it and optionally
change its label or show a spinner (use mode and existing className to keep
styling consistent); this prevents duplicate clicks while the deferred
insertion/application runs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 02ca3ba6-5a54-43c2-8a04-9cc3aef58d3d

📥 Commits

Reviewing files that changed from the base of the PR and between c4a6087 and 6b352d8.

📒 Files selected for processing (5)
  • interactions.php
  • src/editor/components/interaction-panel/index.js
  • src/editor/components/timeline/index.js
  • src/editor/interaction-library/configure-modal.js
  • src/editor/interaction-library/index.js
✅ Files skipped from review due to trivial changes (1)
  • src/editor/components/timeline/index.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/editor/components/interaction-panel/index.js
  • interactions.php

Comment thread src/editor/interaction-library/configure-modal.js
github-actions Bot added a commit that referenced this pull request Jun 1, 2026
@bfintal
Copy link
Copy Markdown
Collaborator

bfintal commented Jun 2, 2026

@coderabbitai review this PR, update your comments

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

@bfintal Sure, I'll review the PR and update my comments now!

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
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: 4

♻️ Duplicate comments (1)
src/editor/plugins/top-toolbar-button/add-interaction-button.js (1)

26-26: ⚠️ Potential issue | 🟠 Major

Selector mismatch issue remains unresolved.

A previous review identified that code in src/editor/plugins/top-toolbar-button/index.js searches for .interact-insert-library-button__wrapper, but this selector does not match any element in the codebase. The button uses interact-insert-library-button (without __wrapper), and the wrapper div uses interact-add-interaction-button-wrapper. This className change does not address the underlying selector issue.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/editor/plugins/top-toolbar-button/add-interaction-button.js` at line 26,
The selector used in src/editor/plugins/top-toolbar-button/index.js
('.interact-insert-library-button__wrapper') doesn't match any DOM class; update
the code so the selector targets the actual element class or rename the element
to match the selector — specifically either change the selector to
'.interact-add-interaction-button-wrapper' (to match the wrapper div) or
add/rename the wrapper element to have the class
'interact-insert-library-button__wrapper'; locate references in index.js and the
button component that set className 'interact-insert-library-button' and
'interact-add-interaction-button-wrapper' and make them consistent.
🧹 Nitpick comments (1)
src/editor/interaction-library/configure-modal.js (1)

151-153: ⚡ Quick win

Consider adding user-facing error notification.

While the error-handling structure is now correct (errors are caught, modal doesn't close, sidebar doesn't open), errors are only logged to the console. Users receive no feedback when the interaction application fails, which creates a poor experience.

Consider dispatching a WordPress notice to inform the user:

} catch ( error ) {
	console.error( 'Interactions Library configuration error:', error.message ) // eslint-disable-line no-console
	dispatch( 'core/notices' ).createNotice(
		'error',
		__( 'Failed to apply interaction. Please check the console for details.', 'interactions' ),
		{
			type: 'snackbar',
			isDismissible: true,
		}
	)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/editor/interaction-library/configure-modal.js` around lines 151 - 153,
The catch block in configure-modal.js currently only logs errors to the console;
update it to also dispatch a WordPress admin notice so users receive feedback:
keep the existing console.error line, then call
dispatch('core/notices').createNotice to create an 'error' notice with a
user-friendly translated message (use __ with the 'interactions' text domain),
and pass options { type: 'snackbar', isDismissible: true } so it appears as a
dismissible snackbar; ensure dispatch and __ are imported/available in the
module before using them.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/editor/components/guided-modal-tour/editor.scss`:
- Line 14: Adjust blank lines to satisfy stylelint: add an empty line before the
declaration "left: var(--left);" and before the comments flagged by
scss/double-slash-comment-empty-line-before, and remove the extra empty line
immediately before the "display: none;" declaration so there is no blank line
preceding it; locate these by finding the "left: var(--left);" and "display:
none;" declarations and the nearby // comments in the guided-modal-tour SCSS and
add/remove blank lines accordingly to meet `declaration-empty-line-before` and
`scss/double-slash-comment-empty-line-before` rules.

In `@src/editor/components/modal-tour/index.js`:
- Around line 349-351: The ternary computing glowTargetSize contains an
unreachable check (the second branch rechecks targetRect.width > 300), so update
the middle condition to use a lower width threshold (e.g., targetRect.width >
100) so medium catches wide-but-short elements; locate the glowTargetSize
expression in src/editor/components/modal-tour/index.js and change the middle
conditional from targetRect.width > 300 || targetRect.height > 100 to
targetRect.width > 100 || targetRect.height > 100 (keeping the existing 'large'
and 'small' branches unchanged).

In `@src/editor/getting-started.php`:
- Around line 27-44: The registered setting interact_guided_tour_states is
exposed via show_in_rest which causes Editor/Author users to get 403 when
frontend JS (models.Settings.save in
src/editor/components/guided-modal-tour/index.js) attempts to persist to
/wp/v2/settings; update the registration in getting-started.php so that
show_in_rest and/or registration only happens for users with
current_user_can('manage_options') (or skip exposing it and gate the localized
guidedTourStates with current_user_can), or alternatively switch persistence to
a per-user mechanism (store in user meta via update_user_meta and a
capability-appropriate REST callback or custom route) and adjust
sanitize_array_setting usage accordingly. Ensure references:
interact_guided_tour_states, sanitize_array_setting, and the guided-modal-tour
component are updated to match the chosen approach.

In `@src/editor/interaction-library/configure-modal.js`:
- Around line 86-88: The early return in the setTimeout callback after calling
parse() (when block is falsy) leaves the modal in a broken state; instead of
returning from the callback, throw a descriptive Error so the surrounding
try-catch in the parent function will catch it and run the existing error
handling/cleanup. Locate the setTimeout callback that calls parse() and
currently does "if (!block) { return }" and replace that return with throwing an
Error (e.g., throw new Error('Failed to parse block in configure modal')), so
the try-catch surrounding the operation can close the modal, open the sidebar,
and show the error to the user.

---

Duplicate comments:
In `@src/editor/plugins/top-toolbar-button/add-interaction-button.js`:
- Line 26: The selector used in src/editor/plugins/top-toolbar-button/index.js
('.interact-insert-library-button__wrapper') doesn't match any DOM class; update
the code so the selector targets the actual element class or rename the element
to match the selector — specifically either change the selector to
'.interact-add-interaction-button-wrapper' (to match the wrapper div) or
add/rename the wrapper element to have the class
'interact-insert-library-button__wrapper'; locate references in index.js and the
button component that set className 'interact-insert-library-button' and
'interact-add-interaction-button-wrapper' and make them consistent.

---

Nitpick comments:
In `@src/editor/interaction-library/configure-modal.js`:
- Around line 151-153: The catch block in configure-modal.js currently only logs
errors to the console; update it to also dispatch a WordPress admin notice so
users receive feedback: keep the existing console.error line, then call
dispatch('core/notices').createNotice to create an 'error' notice with a
user-friendly translated message (use __ with the 'interactions' text domain),
and pass options { type: 'snackbar', isDismissible: true } so it appears as a
dismissible snackbar; ensure dispatch and __ are imported/available in the
module before using them.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9d64d09c-460b-4688-8a69-27aef5267722

📥 Commits

Reviewing files that changed from the base of the PR and between 8ddf5d6 and 385457f.

📒 Files selected for processing (24)
  • interactions.php
  • src/editor/components/guided-modal-tour/README.MD
  • src/editor/components/guided-modal-tour/editor.scss
  • src/editor/components/guided-modal-tour/index.js
  • src/editor/components/guided-modal-tour/tour-conditions.js
  • src/editor/components/guided-modal-tour/util.js
  • src/editor/components/index.js
  • src/editor/components/interaction-panel/index.js
  • src/editor/components/modal-tour/index.js
  • src/editor/components/modal-tour/tour-steps.js
  • src/editor/components/modal-tour/tours/README.md
  • src/editor/components/modal-tour/tours/editor.js
  • src/editor/components/modal-tour/tours/index.js
  • src/editor/components/modal-tour/tours/interaction-library.js
  • src/editor/components/modal-tour/tours/sidebar.js
  • src/editor/components/timeline/index.js
  • src/editor/editor.js
  • src/editor/getting-started.php
  • src/editor/interaction-library/configure-modal.js
  • src/editor/interaction-library/index.js
  • src/editor/interaction-library/select-modal.js
  • src/editor/plugins/block-toolbar-button/index.js
  • src/editor/plugins/top-toolbar-button/add-interaction-button.js
  • src/editor/plugins/top-toolbar-button/index.js

Comment thread src/editor/components/guided-modal-tour/editor.scss
Comment thread src/editor/components/modal-tour/index.js Outdated
Comment thread src/editor/getting-started.php
Comment thread src/editor/interaction-library/configure-modal.js
github-actions Bot added a commit that referenced this pull request Jun 3, 2026
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.

2 participants