diff --git a/modules/admin-home/assets/js/hello-elementor-conversion-banner.js b/modules/admin-home/assets/js/hello-elementor-conversion-banner.js index 35e6efcd..e2ffa480 100644 --- a/modules/admin-home/assets/js/hello-elementor-conversion-banner.js +++ b/modules/admin-home/assets/js/hello-elementor-conversion-banner.js @@ -1,13 +1,18 @@ +import apiFetch from '@wordpress/api-fetch'; import { createRoot } from 'react-dom/client'; +import { useLayoutEffect } from 'react'; import { ThemeProvider } from '@elementor/ui/styles'; import { Welcome } from './components/paper/welcome'; import { AdminProvider } from './providers/admin-provider'; -import Box from '@elementor/ui/Box'; -const App = () => { +const App = ({ config, container }) => { + useLayoutEffect(() => { + container.style.visibility = 'visible'; + }, [container]); + return ( - + { ); }; -document.addEventListener('DOMContentLoaded', () => { - const container = document.getElementById('ehe-admin-cb'); - - if (container) { - const { beforeWrap = false } = window.ehe_cb; - const { selector, before = false } = window.ehe_cb.data; - const headerEnd = document.querySelector(selector); - - if (headerEnd) { - if (beforeWrap) { - const wrapElement = document.querySelector('.wrap'); - if (wrapElement) { - wrapElement.insertAdjacentElement('beforebegin', container); - } - } else if (before) { - headerEnd.insertAdjacentElement('beforebegin', container); - } else { - headerEnd.insertAdjacentElement('afterend', container); - } +const insertBanner = (container, placement) => { + const { beforeWrap = false, selector, before = false } = placement; + + if (beforeWrap) { + const wrapElement = document.querySelector('.wrap'); + + if (!wrapElement) { + return false; + } + + wrapElement.insertAdjacentElement('beforebegin', container); + return true; + } + + if (before) { + const anchor = document.querySelector(selector); + + if (!anchor) { + return false; } - const root = createRoot(container); - root.render(); + anchor.insertAdjacentElement('beforebegin', container); + return true; + } + + const pageTitleAction = document.querySelector('.wrap .page-title-action'); + + if (pageTitleAction) { + pageTitleAction.insertAdjacentElement('afterend', container); + return true; + } + + const headerEnd = document.querySelector(selector); + + if (!headerEnd) { + return false; + } + + headerEnd.insertAdjacentElement('afterend', container); + return true; +}; + +const init = async () => { + if ('undefined' === typeof window.ehe_cb) { + return; + } + + let config; + + try { + const response = await apiFetch({ + path: '/elementor-hello-elementor/v1/admin-settings', + }); + config = response.config; + } catch (e) { + return; + } + + if (!config?.welcome?.title) { + return; } -}); + + const { beforeWrap = false } = window.ehe_cb; + const { selector, before = false } = window.ehe_cb.data; + + const container = document.createElement('div'); + container.id = 'ehe-admin-cb'; + container.className = 'ehe-admin-cb'; + container.style.visibility = 'hidden'; + + if (!insertBanner(container, { beforeWrap, selector, before })) { + return; + } + + const root = createRoot(container); + root.render(); +}; + +if ('loading' === document.readyState) { + document.addEventListener('DOMContentLoaded', init); +} else { + init(); +} diff --git a/modules/admin-home/assets/js/providers/admin-provider.js b/modules/admin-home/assets/js/providers/admin-provider.js index b5f929d4..55646a7a 100644 --- a/modules/admin-home/assets/js/providers/admin-provider.js +++ b/modules/admin-home/assets/js/providers/admin-provider.js @@ -3,11 +3,17 @@ import apiFetch from '@wordpress/api-fetch'; export const AdminContext = createContext(); -export const AdminProvider = ({ children }) => { - const [isLoading, setIsLoading] = React.useState(true); - const [adminSettings, setAdminSettings] = React.useState({}); +export const AdminProvider = ({ children, initialSettings = null }) => { + const [isLoading, setIsLoading] = React.useState(!initialSettings); + const [adminSettings, setAdminSettings] = React.useState( + initialSettings || {}, + ); useEffect(() => { + if (initialSettings) { + return; + } + apiFetch({ path: '/elementor-hello-elementor/v1/admin-settings' }) .then((settings) => { setAdminSettings(settings.config); @@ -15,7 +21,7 @@ export const AdminProvider = ({ children }) => { .finally(() => { setIsLoading(false); }); - }, []); + }, [initialSettings]); return ( -
-
- [ 'selector' => '#wpbody #wpbody-content .wrap h1' ], @@ -199,10 +192,6 @@ public function __construct() { return; } - add_action( 'in_admin_header', function () { - $this->render_conversion_banner(); - }, 11 ); - add_action( 'admin_enqueue_scripts', function () use ( $conversion_banner_active ) { $this->enqueue_scripts( $conversion_banner_active ); } ); diff --git a/tests/playwright/tests/conversion-banner-layout.test.ts b/tests/playwright/tests/conversion-banner-layout.test.ts new file mode 100644 index 00000000..efcd4ff1 --- /dev/null +++ b/tests/playwright/tests/conversion-banner-layout.test.ts @@ -0,0 +1,149 @@ +import { parallelTest as test } from '../parallelTest.ts'; +import { expect } from '@playwright/test'; +import WpAdminPage from '../pages/wp-admin-page.ts'; +import { timeouts } from '../config/timeouts.ts'; + +const emptyWelcomeAdminSettings = { + config: { + welcome: [], + config: { + nonceInstall: 'test-nonce', + slug: 'elementor', + }, + }, +}; + +test.describe('Conversion banner flows [ED-25235]', () => { + test('Pages list does not mount banner when welcome config is empty', async ({ + page, + apiRequests, + }, testInfo) => { + await page.route( + '**/elementor-hello-elementor/v1/admin-settings**', + async (route) => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify(emptyWelcomeAdminSettings), + }); + }, + ); + + const wpAdmin = new WpAdminPage(page, testInfo, apiRequests); + + await wpAdmin.login(); + + await page.goto('/wp-admin/edit.php?post_type=page', { + waitUntil: 'networkidle', + timeout: timeouts.longAction, + }); + + await page.waitForSelector('.wrap h1.wp-heading-inline', { + timeout: timeouts.longAction, + }); + + await page.waitForTimeout(timeouts.longAction / 5); + + await expect(page.locator('#ehe-admin-cb')).toHaveCount(0); + + const headingBox = await page + .locator('.wrap h1.wp-heading-inline') + .boundingBox(); + const actionBox = await page + .locator('.wrap .page-title-action') + .first() + .boundingBox(); + + expect(headingBox).not.toBeNull(); + expect(actionBox).not.toBeNull(); + + if (headingBox && actionBox) { + expect(actionBox.y).toBeLessThan(headingBox.y + headingBox.height + 8); + } + }); + + test('Hello settings page still loads without conversion banner regressions', async ({ + page, + apiRequests, + }, testInfo) => { + const wpAdmin = new WpAdminPage(page, testInfo, apiRequests); + + await wpAdmin.login(); + + await page.goto('/wp-admin/admin.php?page=hello-elementor-settings', { + waitUntil: 'networkidle', + timeout: timeouts.longAction, + }); + + await expect(page.locator('#ehe-admin-settings')).toBeVisible({ + timeout: timeouts.longAction, + }); + + const bannerCount = await page.locator('#ehe-admin-cb').count(); + + if (bannerCount > 0) { + const action = page.locator('.wrap .page-title-action').first(); + const banner = page.locator('#ehe-admin-cb'); + + if ((await action.count()) > 0) { + const isAfterAction = await page.evaluate(() => { + const actionNode = document.querySelector('.wrap .page-title-action'); + const bannerNode = document.getElementById('ehe-admin-cb'); + + return Boolean( + actionNode && + bannerNode && + actionNode.nextElementSibling === bannerNode, + ); + }); + + expect(isAfterAction).toBe(true); + } + } + }); + + test('Plugins page keeps native title row when banner is absent', async ({ + page, + apiRequests, + }, testInfo) => { + const wpAdmin = new WpAdminPage(page, testInfo, apiRequests); + + await wpAdmin.login(); + + await page.goto('/wp-admin/plugins.php', { + waitUntil: 'networkidle', + timeout: timeouts.longAction, + }); + + await page.waitForSelector('.wrap h1', { + timeout: timeouts.longAction, + }); + + await page.waitForTimeout(timeouts.longAction / 5); + + const banner = page.locator('#ehe-admin-cb'); + const bannerCount = await banner.count(); + + if (bannerCount > 0) { + const heading = page.locator('.wrap h1').first(); + const action = page.locator('.wrap .page-title-action').first(); + + if ((await action.count()) > 0) { + const isAfterAction = await page.evaluate(() => { + const actionNode = document.querySelector('.wrap .page-title-action'); + const bannerNode = document.getElementById('ehe-admin-cb'); + + return Boolean( + actionNode && + bannerNode && + actionNode.nextElementSibling === bannerNode, + ); + }); + + expect(isAfterAction).toBe(true); + } + + await expect(heading).toBeVisible(); + } + }); +});