Commit 4343ca5bc7 for woocommerce
commit 4343ca5bc72d0a847698c11bc3db42d258ccc010
Author: Rostislav Wolný <1082140+costasovo@users.noreply.github.com>
Date: Fri May 30 09:37:30 2025 +0200
Update telemetry tracking in the email editor (#58294)
* Restore tracking of editor_layout_loaded event
WOOPLUG-3953
* Add a mechanism for tracking actions on stores and track setDeviceType
WOOPLUG-3953
* Restore header_preview_dropdown_send_test_email_selected event
WOOPLUG-3953
* Add a simple mechanism for tracking clicks and track open preview
WOOPLUG-3953
* Restor tracking of moving post to trash
WOOPLUG-3953
* Improve naming and add commentes to store actions tracking code
WOOPLUG-3953
* Restore tracking of inserter and listview sidebars
WOOPLUG-3953
* Restore tracking of header_blocks_tool_button_clicked
WOOPLUG-3953
* Restore tracking of header_more_menu_dropdown_toggle
WOOPLUG-3953
* Add tracking of feature toggled in more menu
WOOPLUG-3953
* Restore trackig of header_save_all_button_clicked event
WOOPLUG-3953
* Restore tracking of header_save_email_button_clicked
WOOPLUG-3953
* Restore tracking of closing inserter sidebar
WOOPLUG-3953
* Add tracking of inserting blocks and patterns
WOOPLUG-3953
* Restore tracking of header_preview_dropdown_clicked
WOOPLUG-3953
* Reintroduce Email,Block tabs tracking in the sidebar
WOOPLUG-3953
* Refactor list view, open, close tracking, and inserter open tracking
This commit unifies this event with the rest of the events that track
isOpened within the event data instead of tracking separate events
for open and close.
It also moves the tracking to the DOM to track only interactions
via the buttons in the header. The panels are sometimes programmatically closed
for example, when a user selects distraction-free mode.
WOOPLUG-3953
* Add e2e test to verify selectors used for tracking are present
WOOPLUG-3953
* Fix email editor page render PHP error in PHP 7.4
PHP 7.4 doesn't support array spread.
error: Uncaught Error: Cannot unpack array with string keys
WOOPLUG-3953
* Update name of save email button event
It is no longes save all button but just save button
WOOPLUG-3953
* Add documentation page for telemetry tracking
WOOPLUG-3953
* Add tracking for command menu
WOOPLUG-3953
* Add changelog
WOOPLUG-3953
* Fix the broken open preview to a new tab
This could potentialy cause multiple issues for cases where
code relied on the returned value of an action.
WOOPLUG-3953
* Add tracking of the show template toggle in the preview menu
WOOPLUG-3953
* Add tracking of email status change
Currently, the email editor is still not fully refactored to a module
that exports functions, so I followed the principle used for RichTextButton
and passed tracking functionality via a filter.
I consider this a temporary solution until we add exports to the email editor package.
WOOPLUG-3953
* Add tracking of recipients settings in the editor integration
WOOPLUG-3953
* Add tracking of Sender options in template mode's sidebar
WOOPLUG-3953
diff --git a/packages/js/email-editor/changelog/wooplug-3953-update-telemetry-tracking-in-the-email-editor b/packages/js/email-editor/changelog/wooplug-3953-update-telemetry-tracking-in-the-email-editor
new file mode 100644
index 0000000000..078dc5e203
--- /dev/null
+++ b/packages/js/email-editor/changelog/wooplug-3953-update-telemetry-tracking-in-the-email-editor
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Update telemetry tracking to restore events removed by refactoring components
diff --git a/packages/js/email-editor/src/components/block-editor/editor.tsx b/packages/js/email-editor/src/components/block-editor/editor.tsx
index b7f769e661..58c7818744 100644
--- a/packages/js/email-editor/src/components/block-editor/editor.tsx
+++ b/packages/js/email-editor/src/components/block-editor/editor.tsx
@@ -36,6 +36,7 @@ import { PublishSave } from '../../hacks/publish-save';
import { EditorNotices } from '../notices';
import { BlockCompatibilityWarnings } from '../sidebar';
import { BackButtonContent } from '../header/back-button-content';
+import { recordEventOnce } from '../../events';
export function InnerEditor( {
postId: initialPostId,
@@ -115,6 +116,8 @@ export function InnerEditor( {
);
}
+ recordEventOnce( 'editor_layout_loaded' );
+
return (
<SlotFillProvider>
{ /* @ts-expect-error canCopyContent is missing in @types/wordpress__editor */ }
diff --git a/packages/js/email-editor/src/components/preview/send-preview.tsx b/packages/js/email-editor/src/components/preview/send-preview.tsx
index 300673fb9f..8ae03bd0f3 100644
--- a/packages/js/email-editor/src/components/preview/send-preview.tsx
+++ b/packages/js/email-editor/src/components/preview/send-preview.tsx
@@ -15,6 +15,7 @@ import {
*/
import { storeName } from '../../store/constants';
import { SendPreviewEmail } from './send-preview-email';
+import { recordEvent } from '../../events';
export function SendPreview() {
const { togglePreviewModal } = useDispatch( storeName );
@@ -24,6 +25,9 @@ export function SendPreview() {
<PluginPreviewMenuItem
icon={ external }
onClick={ () => {
+ recordEvent(
+ 'header_preview_dropdown_send_test_email_selected'
+ );
togglePreviewModal( true );
} }
>
diff --git a/packages/js/email-editor/src/components/sidebar/settings-panel.tsx b/packages/js/email-editor/src/components/sidebar/settings-panel.tsx
index 46e3f78661..013c179467 100644
--- a/packages/js/email-editor/src/components/sidebar/settings-panel.tsx
+++ b/packages/js/email-editor/src/components/sidebar/settings-panel.tsx
@@ -14,15 +14,28 @@ import {
*/
import { RichTextWithButton } from '../personalization-tags/rich-text-with-button';
import { TemplateSelection } from './template-selection';
+import {
+ recordEvent,
+ recordEventOnce,
+ debouncedRecordEvent,
+} from '../../events';
+
+const tracking = {
+ recordEvent,
+ recordEventOnce,
+ debouncedRecordEvent,
+};
const SidebarExtensionComponent = applyFilters(
'woocommerce_email_editor_setting_sidebar_extension_component',
- RichTextWithButton
+ RichTextWithButton,
+ tracking
) as () => JSX.Element;
const EmailStatusComponent = applyFilters(
'woocommerce_email_editor_setting_sidebar_email_status_component',
- () => null
+ () => null,
+ tracking
) as () => JSX.Element;
export function SettingsPanel() {
diff --git a/packages/js/email-editor/src/components/sidebar/template-settings-panel.tsx b/packages/js/email-editor/src/components/sidebar/template-settings-panel.tsx
index ca920d9f39..434a6744d3 100644
--- a/packages/js/email-editor/src/components/sidebar/template-settings-panel.tsx
+++ b/packages/js/email-editor/src/components/sidebar/template-settings-panel.tsx
@@ -10,16 +10,32 @@ import {
ErrorBoundary,
} from '@wordpress/editor';
+/**
+ * Internal dependencies
+ */
+import {
+ recordEvent,
+ recordEventOnce,
+ debouncedRecordEvent,
+} from '../../events';
+
interface TemplatePanelSection {
id: string;
render: () => JSX.Element | null;
}
+const tracking = {
+ recordEvent,
+ recordEventOnce,
+ debouncedRecordEvent,
+};
+
export function TemplateSettingsPanel() {
// Allow plugins to add custom template sections
const templateSections = applyFilters(
'woocommerce_email_editor_template_sections',
- []
+ [],
+ tracking
) as TemplatePanelSection[];
if ( templateSections.length === 0 ) {
diff --git a/packages/js/email-editor/src/editor.tsx b/packages/js/email-editor/src/editor.tsx
index 51c89731e3..184197f02f 100644
--- a/packages/js/email-editor/src/editor.tsx
+++ b/packages/js/email-editor/src/editor.tsx
@@ -15,7 +15,11 @@ import { InnerEditor } from './components/block-editor';
import { createStore, storeName, editorCurrentPostType } from './store';
import { initHooks } from './editor-hooks';
import { initTextHooks } from './text-hooks';
-import { initEventCollector } from './events';
+import {
+ initEventCollector,
+ initStoreTracking,
+ initDomTracking,
+} from './events';
import { useContentValidation } from './hooks/use-content-validation';
import './style.scss';
@@ -51,6 +55,8 @@ export function initialize( elementId: string ) {
return;
}
initEventCollector();
+ initStoreTracking();
+ initDomTracking();
createStore();
initializeLayout();
initBlocks();
diff --git a/packages/js/email-editor/src/events/dom-tracking.ts b/packages/js/email-editor/src/events/dom-tracking.ts
new file mode 100644
index 0000000000..e535d718a1
--- /dev/null
+++ b/packages/js/email-editor/src/events/dom-tracking.ts
@@ -0,0 +1,162 @@
+/**
+ * External dependencies
+ */
+import { applyFilters } from '@wordpress/hooks';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { recordEvent } from '.';
+
+let EVENTS_TO_TRACK = [];
+
+/**
+ * Filter events by selector and record the event.
+ */
+function trackMatchingEvents( event: Event ) {
+ EVENTS_TO_TRACK.forEach( ( candidate ) => {
+ const matchedTarget = ( event.target as Element )?.matches?.(
+ candidate.selector
+ )
+ ? event.target
+ : ( event.target as Element )?.closest?.( candidate.selector );
+
+ // Event doesn't match any of our watched selectors so we skip it
+ if ( ! matchedTarget ) {
+ return;
+ }
+
+ if ( typeof candidate.track === 'function' ) {
+ candidate.track( matchedTarget, event );
+ } else {
+ recordEvent( candidate.track );
+ }
+ } );
+}
+
+export function initDomTracking() {
+ const isEventTrackingEnabled = applyFilters(
+ 'woocommerce_email_editor_events_tracking_enabled',
+ false
+ );
+ if ( ! isEventTrackingEnabled ) {
+ return;
+ }
+
+ /**
+ * Events to track.
+ * Properties:
+ * - track: The event to track of callback to handle tracking.
+ * - selector: The selector to match the event.
+ */
+ EVENTS_TO_TRACK = [
+ // Header preview dropdown preview in new tab selected
+ {
+ track: 'header_preview_dropdown_preview_in_new_tab_selected',
+ selector: '.editor-preview-dropdown__button-external',
+ },
+ // Header toggle block tools
+ {
+ track: () => {
+ const isBlockToolsCollapsed = document.getElementsByClassName(
+ 'is-collapsed editor-collapsible-block-toolbar'
+ ).length;
+ recordEvent( 'header_blocks_tool_button_clicked', {
+ isBlockToolsCollapsed,
+ } );
+ },
+ selector: '.editor-collapsible-block-toolbar__toggle',
+ },
+ // Header more menu toggle
+ {
+ track: ( target ) => {
+ const isOpened = target.classList.contains( 'is-opened' );
+ recordEvent( 'header_more_menu_dropdown_toggle', {
+ isOpened,
+ } );
+ },
+ // eslint-disable-next-line @wordpress/i18n-text-domain
+ selector: `.components-dropdown-menu__toggle[aria-label="${ __(
+ 'Options'
+ ) }"]`,
+ },
+ // Header save button clicked
+ {
+ track: ( target ) => {
+ if (
+ // eslint-disable-next-line @wordpress/i18n-text-domain
+ ( target.textContent === __( 'Save' ) &&
+ target.getAttribute( 'aria-disabled' ) === 'false' ) ||
+ // eslint-disable-next-line @wordpress/i18n-text-domain
+ target.textContent === __( 'Saving…' )
+ ) {
+ recordEvent( 'header_save_button_clicked' );
+ }
+ },
+ selector: '.editor-post-publish-button',
+ },
+ // Header save draft button clicked
+ {
+ track: 'header_save_email_button_clicked',
+ selector: '.editor-post-saved-state.is-saving',
+ },
+ // Inserter panel close icon clicked
+ {
+ track: 'inserter_sidebar_library_close_icon_clicked',
+ selector:
+ '.block-editor-inserter__menu .block-editor-tabbed-sidebar__close-button',
+ },
+ // Preview dropdown toggle clicked
+ {
+ track: ( target ) => {
+ const isOpened = target.classList.contains( 'is-opened' );
+ recordEvent( 'header_preview_dropdown_clicked', {
+ isOpened,
+ } );
+ },
+ selector: '.editor-preview-dropdown__toggle',
+ },
+ // Email tab in the sidebar clicked
+ {
+ track: () => {
+ recordEvent( 'sidebar_tab_selected', { tab: 'document' } );
+ },
+ selector: '[data-tab-id="edit-post/document"]',
+ },
+ // Block tab in the sidebar clicked
+ {
+ track: () => {
+ recordEvent( 'sidebar_tab_selected', { tab: 'block' } );
+ },
+ selector: '[data-tab-id="edit-post/block"]',
+ },
+ // Header inserter sidebar toggle clicked
+ {
+ track: ( target ) => {
+ const isOpened = target.classList.contains( 'is-pressed' );
+ recordEvent( 'header_inserter_sidebar_clicked', { isOpened } );
+ },
+ selector: '.editor-document-tools__inserter-toggle',
+ },
+ // Header listview sidebar toggle clicked
+ {
+ track: ( target ) => {
+ const isOpened = target.classList.contains( 'is-pressed' );
+ recordEvent( 'header_listview_sidebar_clicked', { isOpened } );
+ },
+ selector: '.editor-document-tools__document-overview-toggle',
+ },
+ // Command in command bar selected
+ {
+ track: ( target ) => {
+ recordEvent( 'command_bar_command_clicked', {
+ command: target.dataset?.value,
+ } );
+ },
+ selector: '.commands-command-menu__container [role="option"]',
+ },
+ ];
+
+ document.addEventListener( 'click', trackMatchingEvents );
+}
diff --git a/packages/js/email-editor/src/events/index.ts b/packages/js/email-editor/src/events/index.ts
index 6f4ec2fd52..e142651271 100644
--- a/packages/js/email-editor/src/events/index.ts
+++ b/packages/js/email-editor/src/events/index.ts
@@ -4,3 +4,5 @@ export {
debouncedRecordEvent,
} from './event-pipeline';
export * from './event-collector';
+export * from './store-tracking';
+export * from './dom-tracking';
diff --git a/packages/js/email-editor/src/events/store-tracking.ts b/packages/js/email-editor/src/events/store-tracking.ts
new file mode 100644
index 0000000000..38fc1c3aeb
--- /dev/null
+++ b/packages/js/email-editor/src/events/store-tracking.ts
@@ -0,0 +1,182 @@
+/**
+ * External dependencies
+ */
+import { use, select } from '@wordpress/data';
+import { applyFilters } from '@wordpress/hooks';
+import { store as preferencesStore } from '@wordpress/preferences';
+import { store as editorStore } from '@wordpress/editor';
+import { __ } from '@wordpress/i18n';
+/**
+ * Internal dependencies
+ */
+import { recordEvent } from '.';
+import { editorCurrentPostType, editorCurrentPostId } from '../store';
+
+/**
+ * Handler functions for tracking individual events recorder by the listening to store actions.
+ */
+const trackSetDeviceType = ( deviceType: string ) => {
+ recordEvent(
+ `header_preview_dropdown_${ deviceType.toLowerCase() }_selected`
+ );
+};
+
+const trackDeleteEntityRecord = ( _entity, type, id ) => {
+ if ( type === editorCurrentPostType && id === editorCurrentPostId ) {
+ recordEvent( 'trash_modal_move_to_trash_button_clicked' );
+ }
+};
+
+const trackSetPreference = ( scope, name, value ) => {
+ const valueBeforeToggle = select( preferencesStore ).get( scope, name );
+ if ( valueBeforeToggle === value ) {
+ return;
+ }
+ const trackedPreferences = {
+ focusMode: 'focus_mode_toggle',
+ fullscreenMode: 'full_screen_mode_toggle',
+ distractionFree: 'distraction_free_toggle',
+ fixedToolbar: 'fixed_toolbar_toggle',
+ };
+ if ( trackedPreferences[ name ] ) {
+ recordEvent( trackedPreferences[ name ], { isEnabled: value } );
+ }
+};
+
+const trackBlockAndPatternInsertion = ( ...args ) => {
+ // @ts-expect-error - isInserterOpened is not in editor types
+ const inserterPanelOpened = select( editorStore ).isInserterOpened();
+ const insQuickInsertOpened = !! document.getElementsByClassName(
+ 'block-editor-inserter__quick-inserter'
+ ).length;
+
+ // We are a bit guessing here that user uses inserter panel when it is opened
+ let source = 'other_inserter';
+ if ( inserterPanelOpened ) {
+ source = 'inserter_sidebar';
+ } else if ( insQuickInsertOpened ) {
+ source = 'quick_inserter';
+ }
+ const blockData = args[ 0 ];
+ const meta = args[ 5 ];
+
+ // Single block insertion
+ if (
+ Array.isArray( blockData ) === false &&
+ typeof blockData === 'object'
+ ) {
+ recordEvent( `${ source }_library_block_selected`, {
+ blockName: blockData.name,
+ } );
+ }
+
+ // Patter inserted
+ if ( Array.isArray( blockData ) && meta && meta.patternName ) {
+ recordEvent( `${ source }_library_pattern_selected`, {
+ patternName: meta.patternName,
+ } );
+ }
+};
+
+const trackSetRenderingMode = ( renderingMode: string ) => {
+ // @ts-expect-error - getRenderingMode is not in editor types
+ const currentRenderingMode = select( editorStore ).getRenderingMode();
+ if ( currentRenderingMode === renderingMode ) {
+ return;
+ }
+ const isPreviewDropdownOpened = !! document.querySelector(
+ // eslint-disable-next-line @wordpress/i18n-text-domain
+ `[aria-label="${ __( 'View options' ) }"]`
+ );
+ // We want to track the event only from the dropdown.
+ // The mode might also change when switching between an email content and template.
+ if ( isPreviewDropdownOpened ) {
+ recordEvent( 'preview_dropdown_rendering_mode_changed', {
+ renderingMode,
+ } );
+ }
+};
+
+/**
+ * List of store actions to be tracked.
+ */
+const TRACKED_STORE_EVENTS = {
+ 'core/editor': {
+ autosave: 'editor_content_auto_saved',
+ setDeviceType: trackSetDeviceType,
+ setRenderingMode: trackSetRenderingMode,
+ },
+ core: {
+ deleteEntityRecord: trackDeleteEntityRecord,
+ },
+ 'core/block-editor': {
+ insertBlock: trackBlockAndPatternInsertion,
+ insertBlocks: trackBlockAndPatternInsertion,
+ },
+ 'core/preferences': {
+ set: trackSetPreference,
+ },
+ 'core/commands': {
+ open: 'command_menu_opened',
+ close: 'command_menu_closed',
+ },
+};
+
+const rewrittenActions = {};
+const originalActions = {};
+
+export const initStoreTracking = () => {
+ const isEventTrackingEnabled = applyFilters(
+ 'woocommerce_email_editor_events_tracking_enabled',
+ false
+ );
+
+ if ( ! isEventTrackingEnabled ) {
+ return;
+ }
+
+ use( ( registry ) => ( {
+ dispatch: ( namespace ) => {
+ const storeName =
+ typeof namespace === 'object' ? namespace.name : namespace;
+ const actions = registry.dispatch( storeName );
+ const trackers = TRACKED_STORE_EVENTS[ storeName ];
+
+ if ( ! trackers ) {
+ return actions;
+ }
+
+ // Initialize namespace level objects if not yet done.
+ if ( ! rewrittenActions[ storeName ] ) {
+ rewrittenActions[ storeName ] = {};
+ }
+ if ( ! originalActions[ storeName ] ) {
+ originalActions[ storeName ] = {};
+ }
+
+ for ( const [ action, event ] of Object.entries( trackers ) ) {
+ if ( ! originalActions[ storeName ][ action ] ) {
+ originalActions[ storeName ][ action ] = actions[ action ];
+ rewrittenActions[ storeName ][ action ] = ( ...args ) => {
+ try {
+ if ( typeof event === 'function' ) {
+ event( ...args );
+ } else if ( typeof event === 'string' ) {
+ recordEvent( event );
+ }
+ } catch ( error ) {
+ // eslint-disable-next-line no-console
+ console.error( 'Error tracking event', error );
+ }
+ return originalActions[ storeName ][ action ](
+ ...args
+ );
+ };
+ }
+ actions[ action ] = rewrittenActions[ storeName ][ action ];
+ }
+
+ return actions;
+ },
+ } ) );
+};
diff --git a/packages/js/email-editor/src/telemetry-tracking.md b/packages/js/email-editor/src/telemetry-tracking.md
new file mode 100644
index 0000000000..8c089d9260
--- /dev/null
+++ b/packages/js/email-editor/src/telemetry-tracking.md
@@ -0,0 +1,104 @@
+# Editor Telemetry Tracking
+
+The email editor has a built in system for tracking it's telemetry that can be used to track users interactions with the editor.
+
+## Enabling Tracking
+
+To enable tracking an integrator needs to add the following filters.
+
+```js
+// enable email editor event tracking
+addFilter(
+ 'woocommerce_email_editor_events_tracking_enabled',
+ 'your_plugin_namespace`,
+ () => true
+);
+
+// process events tracked in the editor
+addAction( 'woocommerce_email_editor_events', 'your_plugin_namespace', ( editorEvent ) => {
+ const { name, ...data } = editorEvent;
+ // Replace console.log with your tracking code
+ console.log( name, data );
+```
+
+## Tracked events
+
+All events are prefixed `email_editor_events_`.
+
+### Table of events
+
+| Event Name | Data | Description |
+|------------|------|-------------|
+| `block_controls_personalization_tags_button_clicked` | - | Tracked when the personalization tags button is clicked in block controls |
+| `command_menu_opened` | - | Tracked when command menu is opened via click or a shortcut |
+| `command_menu_closed` | - | Tracked when command menu is closed |
+| `command_bar_command_clicked` | `{ command }` | Tracked when a command is selected by a click. |
+| `edit_template_modal_cancel_button_clicked` | - | Tracked when the cancel button is clicked in the edit template modal |
+| `edit_template_modal_closed` | - | Tracked when the edit template modal is closed |
+| `edit_template_modal_continue_button_clicked` | `{ templateId }` | Tracked when the continue button is clicked in the edit template modal |
+| `edit_template_modal_opened` | - | Tracked when the edit template modal is opened |
+| `editor_layout_loaded` | - | Tracked when the editor layout is loaded |
+| `editor_showed_email_sent_notice` | - | Tracked when the editor shows the email sent notice |
+| `header_blocks_tool_button_clicked` | `{ isOpened }` | Tracked when user clicks the blocks tool button in the header to toggle header block tools |
+| `header_close_button_clicked` | - | Tracked when user clicks the back button in the header |
+| `header_inserter_sidebar_clicked` | `{ isOpened }` | Tracked when user clicks the inserter sidebar button in the header |
+| `header_listview_sidebar_clicked` | `{ isOpened }` | Tracked when user clicks the listview sidebar button in the header |
+| `header_more_menu_dropdown_toggle` | `{ isOpened }` | Tracked when user toggles the more menu dropdown in the header |
+| `header_preview_dropdown_clicked` | `{ isOpened }` | Tracked when user clicks an action in the preview dropdown |
+| `header_preview_dropdown_${deviceType}_selected` | - | Tracked when a device type is selected from the header preview dropdown (e.g., desktop, mobile, tablet) |
+| `header_preview_dropdown_preview_in_new_tab_selected` | - | Tracked when the preview in new tab option is selected from the header preview dropdown |
+| `header_preview_dropdown_send_test_email_selected` | - | Tracked when the send test email option is selected from the header preview dropdown |
+| `header_save_button_clicked` | - | Tracked when user clicks the save button in the header |
+| `header_send_button_clicked` | - | Tracked when user clicks the send button in the header |
+| `personalization_tags_modal_category_menu_clicked` | `{ category, openedBy }` | Tracked when a category is selected in the personalization tags modal |
+| `personalization_tags_modal_closed` | `{ openedBy }` | Tracked when the personalization tags modal is closed |
+| `personalization_tags_modal_learn_more_link_clicked` | `{ openedBy }` | Tracked when the learn more link is clicked in the personalization tags modal |
+| `personalization_tags_modal_opened` | `{ openedBy }` | Tracked when the personalization tags modal is opened. The openedBy parameter indicates where it was opened from (e.g., 'block-controls', 'RichTextWithButton-BaseControl') |
+| `personalization_tags_modal_search_control_input_updated` | `{ openedBy }` | Tracked when the search input is updated in the personalization tags modal |
+| `personalization_tags_modal_tag_insert_button_clicked` | `{ insertedTag, activeCategory, openedBy }` | Tracked when a tag insert button is clicked in the personalization tags modal |
+| `preview_dropdown_rendering_mode_changed` | `{ renderingMode }` | Tracked when user toggles "Show template" in preview menu. Values are 'template-locked', 'post-only'. |
+| `rich_text_with_button_input_field_updated` | `{ attributeName }` | Tracked when the input field is updated in a rich text with button component |
+| `rich_text_with_button_personalization_tags_inserted` | `{ attributeName, value }` | Tracked when a personalization tag is inserted in a rich text with button component |
+| `rich_text_with_button_personalization_tags_shortcode_icon_clicked` | `{ attributeName, label }` | Tracked when the personalization tags shortcode icon is clicked in a rich text with button component |
+| `send_preview_email_modal_check_sending_method_configuration_link_clicked` | - | Tracked when the sending method configuration link is clicked in the send preview email modal |
+| `send_preview_email_modal_closed` | - | Tracked when the send preview email modal is closed |
+| `send_preview_email_modal_close_button_clicked` | - | Tracked when the close button is clicked in the send preview email modal |
+| `send_preview_email_modal_opened` | - | Tracked when the send preview email modal is opened |
+| `send_preview_email_modal_send_test_email_button_clicked` | - | Tracked when the send test email button is clicked in the send preview email modal |
+| `send_preview_email_modal_send_to_field_key_code_enter` | - | Tracked when the enter key is pressed in the send to field of the send preview email modal |
+| `send_preview_email_modal_send_to_field_updated` | - | Tracked when the send to field is updated in the send preview email modal |
+| `send_preview_email_modal_sign_up_for_mailpoet_sending_service_link_clicked` | - | Tracked when the sign up for MailPoet Sending Service link is clicked in the send preview email modal |
+| `sent_preview_email` | `{ postId, email }` | Tracked when a preview email is successfully sent |
+| `sent_preview_email_error` | `{ email }` | Tracked when there's an error sending a preview email |
+| `sidebar_tab_selected` | `{ tab: 'document' \| 'block' }` | Tracked when user selects a tab in the sidebar |
+| `styles_sidebar_navigation_click` | `{ path }` | Tracked when a navigation button is clicked in the styles sidebar (e.g., typography, colors, layout) |
+| `styles_sidebar_screen_colors_opened` | - | Tracked when the colors screen in styles sidebar is opened |
+| `styles_sidebar_screen_colors_styles_updated` | - | Tracked when colors are updated in the styles sidebar |
+| `styles_sidebar_screen_layout_dimensions_block_spacing_reset_clicked` | - | Tracked when the block spacing reset button is clicked in the dimensions panel |
+| `styles_sidebar_screen_layout_dimensions_block_spacing_updated` | `{ value }` | Tracked when block spacing is updated in the dimensions panel |
+| `styles_sidebar_screen_layout_dimensions_padding_reset_clicked` | - | Tracked when the padding reset button is clicked in the dimensions panel |
+| `styles_sidebar_screen_layout_dimensions_padding_updated` | `{ value }` | Tracked when padding is updated in the dimensions panel |
+| `styles_sidebar_screen_layout_dimensions_reset_all_selected` | - | Tracked when the reset all button is clicked in the dimensions panel |
+| `styles_sidebar_screen_layout_opened` | - | Tracked when the layout screen in styles sidebar is opened |
+| `styles_sidebar_screen_typography_button_click` | `{ element, label, path }` | Tracked when a typography element button is clicked in the typography panel |
+| `styles_sidebar_screen_typography_element_heading_level_selected` | `{ value }` | Tracked when a heading level is selected in the typography element screen |
+| `styles_sidebar_screen_typography_element_opened` | `{ element }` | Tracked when the typography screen in styles sidebar is opened |
+| `styles_sidebar_screen_typography_element_panel_reset_all_styles_selected` | `{ element, headingLevel }` | Tracked when the reset all styles button is clicked in the typography element panel |
+| `styles_sidebar_screen_typography_opened` | - | Tracked when the typography screen in styles sidebar is opened |
+| `template_select_modal_category_change` | `{ category }` | Tracked when user changes category in template select modal |
+| `template_select_modal_closed` | `{ templateSelectMode }` | Tracked when template select modal is closed. templateSelectMode tracks category which was opened. |
+| `template_select_modal_handle_close_without_template_selected` | - | Tracked when the template select modal is closed without explicitly selecting a template |
+| `template_select_modal_opened` | `{ templateSelectMode }` | Tracked when template select modal is opened |
+| `template_select_modal_start_from_scratch_clicked` | - | Tracked when the "Start from scratch" button is clicked in the template select modal |
+| `template_select_modal_template_selected` | `{ template }` | Tracked when user selects a template in the template select modal |
+| `trash_modal_move_to_trash_button_clicked` | - | Tracked when user clicks the move to trash button in the trash modal |
+| `styles_sidebar_screen_typography_element_panel_set_letter_spacing` | `{ element, newValue, selectedDefaultLetterSpacing }` | Tracked when letter spacing is changed in typography panel |
+| `styles_sidebar_screen_typography_element_panel_set_line_height` | `{ element, newValue, selectedDefaultLineHeight }` | Tracked when line height is changed in typography panel |
+| `styles_sidebar_screen_typography_element_panel_set_font_size` | `{ element, newValue, selectedDefaultFontSize }` | Tracked when font size is changed in typography panel |
+| `styles_sidebar_screen_typography_element_panel_set_font_family` | `{ element, newValue, selectedDefaultFontFamily }` | Tracked when font family is changed in typography panel |
+| `styles_sidebar_screen_typography_element_panel_set_text_decoration` | `{ element, newValue, selectedDefaultTextDecoration }` | Tracked when text decoration is changed in typography panel |
+| `styles_sidebar_screen_typography_element_panel_set_text_transform` | `{ element, newValue, selectedDefaultTextDecoration }` | Tracked when test transform is changed in typography panel |
+| `styles_sidebar_screen_typography_element_panel_set_font_appearance` | `{ element, newFontStyle, newFontWeight, selectedDefaultFontStyle, selectedDefaultFontWeight }` | Tracked when font appearance is changed in typography panel |
+| `styles_sidebar_screen_typography_element_panel_reset_all_styles_selected` | - | Tracked when all styles are reset |
+| `{$inserter}_library_block_selected` | `{ blockName }` | Tracked when a block is inserted via an inserter. Inserter types: inserter_sidebar, quick_inserter, other_inserter. |
+| `{$inserter}_library_pattern_selected` | `{ patternName }` | Tracked when a pattern is inserted via an inserter. Inserter types: inserter_sidebar, quick_inserter, other_inserter. |
diff --git a/plugins/woocommerce/changelog/wooplug-3953-update-telemetry-tracking-in-the-email-editor b/plugins/woocommerce/changelog/wooplug-3953-update-telemetry-tracking-in-the-email-editor
new file mode 100644
index 0000000000..70d1238370
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-3953-update-telemetry-tracking-in-the-email-editor
@@ -0,0 +1,5 @@
+Significance: patch
+Type: fix
+Comment: Fix email editor PHP error in PHP 7.4 and add test checking selector used for tracking in email editor
+
+
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/email-status.tsx b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/email-status.tsx
index b652ec8f9c..8df765dbcc 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/email-status.tsx
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/email-status.tsx
@@ -21,6 +21,7 @@ import { EMAIL_STATUSES } from '../../settings-email/settings-email-listing-stat
interface EmailStatusProps {
className?: string;
+ recordEvent: ( name: string, data?: Record< string, unknown > ) => void;
}
/**
@@ -30,7 +31,10 @@ interface EmailStatusProps {
* @param {EmailStatusProps} props - Component props.
* @return {JSX.Element} Rendered component.
*/
-export function EmailStatus( { className }: EmailStatusProps ): JSX.Element {
+export function EmailStatus( {
+ className,
+ recordEvent,
+}: EmailStatusProps ): JSX.Element {
const [ woocommerce_email_data ] = useEntityProp(
'postType',
'woo_email',
@@ -69,6 +73,9 @@ export function EmailStatus( { className }: EmailStatusProps ): JSX.Element {
},
}
);
+ recordEvent( 'email_status_changed', {
+ status: newValue ? 'active' : 'inactive',
+ } );
};
const renderDropdownContent = ( {
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/sidebar_settings.tsx b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/sidebar_settings.tsx
index 86fd036868..6c6ec1f340 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/sidebar_settings.tsx
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/sidebar_settings.tsx
@@ -23,8 +23,27 @@ import { EmailStatus } from './email-status';
const previewTextMaxLength = 150;
const previewTextRecommendedLength = 80;
-// @ts-expect-error RichTextWithButton has default any type and is not exported yet.
-const SidebarSettings = ( { RichTextWithButton } ) => {
+type SidebarSettings = {
+ RichTextWithButton: React.ComponentType< {
+ attributeName: string;
+ attributeValue: string;
+ updateProperty: ( name: string, value: string | boolean ) => void;
+ label: string;
+ placeholder: string;
+ help?: React.ReactNode;
+ } >;
+ recordEvent: ( name: string, data?: Record< string, unknown > ) => void;
+ debouncedRecordEvent: (
+ name: string,
+ data?: Record< string, unknown >
+ ) => void;
+};
+
+const SidebarSettings = ( {
+ RichTextWithButton,
+ recordEvent,
+ debouncedRecordEvent,
+}: SidebarSettings ) => {
const [ woocommerce_email_data ] = useEntityProp(
'postType',
'woo_email',
@@ -162,6 +181,9 @@ const SidebarSettings = ( { RichTextWithButton } ) => {
if ( ! value ) {
updateWooMailProperty( 'cc', '' );
}
+ recordEvent( 'email_cc_toggle_clicked', {
+ isEnabled: value,
+ } );
} }
/>
</BaseControl>
@@ -175,6 +197,12 @@ const SidebarSettings = ( { RichTextWithButton } ) => {
value={ woocommerce_email_data?.cc || '' }
onChange={ ( value ) => {
updateWooMailProperty( 'cc', value );
+ debouncedRecordEvent(
+ 'email_cc_input_updated',
+ {
+ value,
+ }
+ );
} }
help={ __(
'Add recipients who will receive a copy of the email. Separate multiple addresses with commas.',
@@ -196,6 +224,9 @@ const SidebarSettings = ( { RichTextWithButton } ) => {
if ( ! value ) {
updateWooMailProperty( 'bcc', '' );
}
+ recordEvent( 'email_bcc_toggle_clicked', {
+ isEnabled: value,
+ } );
} }
/>
</BaseControl>
@@ -209,6 +240,12 @@ const SidebarSettings = ( { RichTextWithButton } ) => {
value={ woocommerce_email_data?.bcc || '' }
onChange={ ( value ) => {
updateWooMailProperty( 'bcc', value );
+ debouncedRecordEvent(
+ 'email_bcc_input_updated',
+ {
+ value,
+ }
+ );
} }
help={ __(
'Add recipients who will receive a hidden copy of the email. Separate multiple addresses with commas.',
@@ -226,14 +263,20 @@ export function modifySidebar() {
addFilter(
'woocommerce_email_editor_setting_sidebar_email_status_component',
NAME_SPACE,
- () => EmailStatus
+ ( _originalComponent, tracking ) => {
+ return () => <EmailStatus recordEvent={ tracking.recordEvent } />;
+ }
);
addFilter(
'woocommerce_email_editor_setting_sidebar_extension_component',
NAME_SPACE,
- ( RichTextWithButton ) => {
+ ( RichTextWithButton, tracking ) => {
return () => (
- <SidebarSettings RichTextWithButton={ RichTextWithButton } />
+ <SidebarSettings
+ RichTextWithButton={ RichTextWithButton }
+ recordEvent={ tracking.recordEvent }
+ debouncedRecordEvent={ tracking.debouncedRecordEvent }
+ />
);
}
);
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/templates/index.tsx b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/templates/index.tsx
index 9bd7ebf363..f606384284 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/templates/index.tsx
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/templates/index.tsx
@@ -13,12 +13,18 @@ function modifyTemplateSidebar() {
addFilter(
'woocommerce_email_editor_template_sections',
'my-plugin/template-settings',
- ( sections ) => [
+ ( sections, tracking ) => [
...sections,
{
id: 'my-custom-section',
render: () => {
- return <TemplateSenderPanel />;
+ return (
+ <TemplateSenderPanel
+ debouncedRecordEvent={
+ tracking.debouncedRecordEvent
+ }
+ />
+ );
},
},
]
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/templates/template_sender_panel.tsx b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/templates/template_sender_panel.tsx
index c5f3d1ddcb..48fb269f85 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/templates/template_sender_panel.tsx
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/templates/template_sender_panel.tsx
@@ -6,7 +6,16 @@ import { PanelRow, TextControl } from '@wordpress/components';
import { useEntityProp } from '@wordpress/core-data';
import { useCallback, useRef } from '@wordpress/element';
-function TemplateSenderPanel() {
+type TemplateSenderPanelProps = {
+ debouncedRecordEvent: (
+ name: string,
+ data?: Record< string, unknown >
+ ) => void;
+};
+
+function TemplateSenderPanel( {
+ debouncedRecordEvent,
+}: TemplateSenderPanelProps ) {
const [ woocommerce_template_data, setWoocommerceTemplateData ] =
useEntityProp( 'postType', 'wp_template', 'woocommerce_data' );
const emailInputRef = useRef< HTMLInputElement >( null );
@@ -20,6 +29,7 @@ function TemplateSenderPanel() {
from_name: value,
},
} );
+ debouncedRecordEvent( 'email_from_name_input_updated', { value } );
},
[ woocommerce_template_data, setWoocommerceTemplateData ]
);
@@ -38,6 +48,9 @@ function TemplateSenderPanel() {
emailInputRef.current.checkValidity();
emailInputRef.current.reportValidity();
}
+ debouncedRecordEvent( 'email_from_address_input_updated', {
+ value,
+ } );
},
[ woocommerce_template_data, setWoocommerceTemplateData ]
);
diff --git a/plugins/woocommerce/src/Internal/EmailEditor/PageRenderer.php b/plugins/woocommerce/src/Internal/EmailEditor/PageRenderer.php
index 42d4fa1816..c1b40cee97 100644
--- a/plugins/woocommerce/src/Internal/EmailEditor/PageRenderer.php
+++ b/plugins/woocommerce/src/Internal/EmailEditor/PageRenderer.php
@@ -157,6 +157,10 @@ class PageRenderer {
)
);
+ $email_editor_settings = $this->settings_controller->get_settings();
+ $email_editor_settings['isFullScreenForced'] = true;
+ $email_editor_settings['displaySendEmailButton'] = false;
+
wp_localize_script(
'woocommerce_email_editor',
'WooCommerceEmailEditor',
@@ -164,11 +168,7 @@ class PageRenderer {
'current_post_type' => esc_js( $post_type ),
'current_post_id' => $post_id,
'current_wp_user_email' => esc_js( $current_user_email ),
- 'editor_settings' => array(
- ...$this->settings_controller->get_settings(),
- 'isFullScreenForced' => true,
- 'displaySendEmailButton' => false,
- ),
+ 'editor_settings' => $email_editor_settings,
'editor_theme' => $this->theme_controller->get_base_theme()->get_raw_data(),
'user_theme_post_id' => $this->user_theme->get_user_theme_post()->ID,
'urls' => array(
diff --git a/plugins/woocommerce/tests/e2e-pw/tests/email/editor-tracking-selectors.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/email/editor-tracking-selectors.spec.js
new file mode 100644
index 0000000000..61cb9a1754
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e-pw/tests/email/editor-tracking-selectors.spec.js
@@ -0,0 +1,142 @@
+const { test, expect, request } = require( '@playwright/test' );
+const { setOption } = require( '../../utils/options' );
+const { getWooEmails } = require( '../../utils/email' );
+const { ADMIN_STATE_PATH } = require( '../../playwright.config' );
+
+const setFeatureFlag = async ( baseURL, value ) =>
+ await setOption(
+ request,
+ baseURL,
+ 'woocommerce_feature_block_email_editor_enabled',
+ value
+ );
+
+/**
+ * The purpose of this test is to alert us if the selectors that are used to track telemetry events in the email editor are changed.
+ *
+ * The test checks that the selectors that are used to track telemetry events in the email editor are present.
+ */
+test.describe( 'WooCommerce Email Editor Tracking Selectors', () => {
+ test.use( { storageState: ADMIN_STATE_PATH } );
+
+ test.afterAll( async ( { baseURL } ) => {
+ await setFeatureFlag( baseURL, 'no' );
+ } );
+
+ test( 'Check selectors for tracking events', async ( {
+ page,
+ baseURL,
+ } ) => {
+ await setFeatureFlag( baseURL, 'yes' );
+
+ // Navigate to WooCommerce Email Settings page to generate email posts
+ await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=email' );
+ const emails = await getWooEmails();
+
+ await page.goto(
+ `wp-admin/post.php?post=${ emails.data[ 0 ].id }&action=edit`
+ );
+
+ // Check that the Editor is present
+ const editorLocator = page.locator( '#woocommerce-email-editor' );
+ await expect( editorLocator ).toBeVisible();
+
+ // Check listview sidebar toggle button selector
+ await expect(
+ editorLocator.locator(
+ '.editor-document-tools__document-overview-toggle'
+ )
+ ).toBeVisible();
+ // Check inserter sidebar toggle button selector
+ await expect(
+ editorLocator.locator( '.editor-document-tools__inserter-toggle' )
+ ).toBeVisible();
+ // Check Email tab in the sidebar
+ await expect(
+ editorLocator.locator( '[data-tab-id="edit-post/block"]' )
+ ).toBeVisible();
+ // Check Block tab in the sidebar
+ await expect(
+ editorLocator.locator( '[data-tab-id="edit-post/document"]' )
+ ).toBeVisible();
+ // Check preview dropdown toggle
+ await expect(
+ editorLocator.locator( '.editor-preview-dropdown__toggle' )
+ ).toBeVisible();
+
+ // Check inserter sidebar close icon
+ // Click inserter sidebar toggle button
+ await editorLocator
+ .locator( '.editor-document-tools__inserter-toggle' )
+ .click();
+ // Check inserter sidebar close icon is now visible after opening
+ await expect(
+ editorLocator.locator(
+ '.block-editor-inserter__menu .block-editor-tabbed-sidebar__close-button'
+ )
+ ).toBeVisible();
+
+ // Check save button selector
+ await expect(
+ editorLocator.locator( '.editor-post-publish-button' )
+ ).toBeVisible();
+
+ // Check more menu toggle selector
+ await expect(
+ editorLocator.locator(
+ '.components-dropdown-menu__toggle[aria-label="Options"]'
+ )
+ ).toBeVisible();
+
+ // Check open preview in new tab button
+ // Click preview dropdown toggle
+ await editorLocator
+ .locator( '.editor-preview-dropdown__toggle' )
+ .click();
+ // Check open in new tab selector
+ await expect(
+ page.locator( '.editor-preview-dropdown__button-external' )
+ ).toBeVisible();
+ // Close preview dropdown
+ await editorLocator
+ .locator( '.editor-preview-dropdown__toggle' )
+ .click();
+
+ // Check command bar button
+ await editorLocator.locator( '.editor-document-bar' ).click();
+ // Fill command bar input with 'a' to get some results
+ await page
+ .locator( '.commands-command-menu__header input' )
+ .fill( 'a' );
+ // Check command selected selector
+ await expect(
+ page.locator(
+ '.commands-command-menu__container [role="option"]:first-child'
+ )
+ ).toBeVisible();
+ // Press Escape key to close command bar
+ await page.keyboard.press( 'Escape' );
+
+ // Check header block tools toggle button
+ // Enable header block tools
+ await editorLocator
+ .locator(
+ '.components-dropdown-menu__toggle[aria-label="Options"]'
+ )
+ .click();
+ // Check header preview dropdown preview in new tab selected
+ // Click header preview dropdown preview in new tab
+ await page
+ .locator( '.components-popover' )
+ .getByText( 'Top toolbar' )
+ .click();
+ // Click canvas to select a block to make top toolbar visible
+ await page
+ .frameLocator( 'iframe[name="editor-canvas"]' )
+ .locator( '.wp-block-heading' )
+ .click();
+ await expect(
+ editorLocator.locator( '.editor-collapsible-block-toolbar__toggle' )
+ ).toBeVisible();
+ } );
+} );
diff --git a/plugins/woocommerce/tests/e2e-pw/utils/email.js b/plugins/woocommerce/tests/e2e-pw/utils/email.js
index 9cf20e0269..9f0a368386 100644
--- a/plugins/woocommerce/tests/e2e-pw/utils/email.js
+++ b/plugins/woocommerce/tests/e2e-pw/utils/email.js
@@ -2,6 +2,7 @@
* Internal dependencies
*/
import { expect } from '../fixtures/fixtures';
+import ApiClient, { WP_API_PATH } from './api-client';
/**
* Check that an email exists in the WP Mail Logging plugin Email Log page. WP Mail Logging plugin must be installed.
@@ -67,3 +68,11 @@ export async function expectEmailContent(
emailContent
);
}
+
+export async function getWooEmails( params ) {
+ const emails = await ApiClient.getInstance().get(
+ `${ WP_API_PATH }/woo_email`,
+ { ...params }
+ );
+ return emails;
+}