diff --git a/mailpoet/tests/acceptance/EmailEditor/CreateAndSendEmailUsingGutenbergCest.php b/mailpoet/tests/acceptance/EmailEditor/CreateAndSendEmailUsingGutenbergCest.php index f121f36101..d1e658c393 100644 --- a/mailpoet/tests/acceptance/EmailEditor/CreateAndSendEmailUsingGutenbergCest.php +++ b/mailpoet/tests/acceptance/EmailEditor/CreateAndSendEmailUsingGutenbergCest.php @@ -52,7 +52,7 @@ class CreateAndSendEmailUsingGutenbergCest { $i->click('[aria-label="Change campaign name"]'); $i->click('Email', '.editor-sidebar__panel-tabs'); $i->fillField('[data-automation-id="email_subject"]', 'My New Subject'); - $i->fillField('[data-automation-id="email_preview_text"]', 'My New Preview Text'); + $i->fillField('[data-automation-id="email_preheader"]', 'My New Preview Text'); $i->wantTo('Send an email and verify it was delivered'); $i->click('Save Draft'); diff --git a/packages/js/email-editor/src/components/personalization-tags/personalization-tags-modal.tsx b/packages/js/email-editor/src/components/personalization-tags/personalization-tags-modal.tsx index fdb226ce8b..614861e73b 100644 --- a/packages/js/email-editor/src/components/personalization-tags/personalization-tags-modal.tsx +++ b/packages/js/email-editor/src/components/personalization-tags/personalization-tags-modal.tsx @@ -1,7 +1,7 @@ import { Modal, SearchControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { PersonalizationTag, storeName } from '../../store'; -import { select } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { external, Icon } from '@wordpress/icons'; import './index.scss'; import { useState } from '@wordpress/element'; @@ -12,12 +12,15 @@ const PersonalizationTagsModal = ( { onInsert, isOpened, closeCallback } ) => { const [ activeCategory, setActiveCategory ] = useState( null ); const [ searchQuery, setSearchQuery ] = useState( '' ); + const list = useSelect( + ( select ) => select( storeName ).getPersonalizationTagsList(), + [] + ); + if ( ! isOpened ) { return null; } - const list = select( storeName ).getPersonalizationTagsList(); - const groupedTags: Record< string, PersonalizationTag[] > = list.reduce( ( groups, item ) => { const { category, name, token } = item; diff --git a/packages/js/email-editor/src/components/personalization-tags/rich-text-with-button.tsx b/packages/js/email-editor/src/components/personalization-tags/rich-text-with-button.tsx new file mode 100644 index 0000000000..44e82aa9d6 --- /dev/null +++ b/packages/js/email-editor/src/components/personalization-tags/rich-text-with-button.tsx @@ -0,0 +1,130 @@ +import { BaseControl, Button } from '@wordpress/components'; +import { PersonalizationTagsModal } from './personalization-tags-modal'; +import { useRef, useState } from '@wordpress/element'; +import { + createTextToHtmlMap, + getCursorPosition, + isMatchingComment, +} from './rich-text-utils'; +import { useDispatch } from '@wordpress/data'; +import { useEntityProp } from '@wordpress/core-data'; +import { storeName } from '../../store'; +import { RichText } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; + +export function RichTextWithButton( { + label, + labelSuffix, + help, + placeholder, + attributeName, +} ) { + const [ mailpoetEmailData ] = useEntityProp( + 'postType', + 'mailpoet_email', + 'mailpoet_data' + ); + + const { updateEmailMailPoetProperty } = useDispatch( storeName ); + + const [ selectionRange, setSelectionRange ] = useState( null ); + const [ isModalOpened, setIsModalOpened ] = useState( false ); + + const richTextRef = useRef( null ); + + const handleInsertPersonalizationTag = async ( value ) => { + // Retrieve the current value of the active RichText + const currentValue = mailpoetEmailData[ attributeName ] ?? ''; + + // Generate text-to-HTML mapping + const { mapping } = createTextToHtmlMap( currentValue ); + // Ensure selection range is within bounds + const start = selectionRange?.start ?? currentValue.length; + const end = selectionRange?.end ?? currentValue.length; + + // Default values for starting and ending indexes. + let htmlStart = start; + let htmlEnd = end; + // If indexes are not matching a comment, update them + if ( ! isMatchingComment( currentValue, start, end ) ) { + htmlStart = mapping[ start ] ?? currentValue.length; + htmlEnd = mapping[ end ] ?? currentValue.length; + } + + // Insert the new tag + const updatedValue = + currentValue.slice( 0, htmlStart ) + + `` + + currentValue.slice( htmlEnd ); + + // Update the corresponding property + updateEmailMailPoetProperty( attributeName, updatedValue ); + + setSelectionRange( null ); + }; + + const finalLabel = ( + <> + { label } +