From 5903f2fcf5ec2522b1705c19b95ba389729eb135 Mon Sep 17 00:00:00 2001
From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com>
Date: Tue, 27 May 2025 18:12:20 +0100
Subject: [PATCH 01/10] Refactor LCP component: Introduce error handling and
tooltip for optimization failures
* Migrate LCP status display logic to a new Status component.
* Add ErrorTooltip component to show detailed error messages for failed optimizations.
* Update LCP state management to include error handling in the backend.
* Introduce new SCSS styles for error tooltips and status display.
* Ensure compatibility with existing LCP state management and API responses.
---
.../app/assets/src/js/features/lcp/lcp.tsx | 64 +-------
.../lcp/lib/stores/lcp-state-types.ts | 7 +-
.../lcp/status/error-tooltip.module.scss | 52 +++++++
.../js/features/lcp/status/error-tooltip.tsx | 30 ++++
.../status.module.scss} | 24 +--
.../src/js/features/lcp/status/status.tsx | 145 ++++++++++++++++++
.../optimizations/lcp/class-lcp-state.php | 18 +++
.../modules/optimizations/lcp/class-lcp.php | 7 +
.../rest-api/endpoints/class-update-lcp.php | 20 ++-
9 files changed, 291 insertions(+), 76 deletions(-)
create mode 100644 projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.module.scss
create mode 100644 projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.tsx
rename projects/plugins/boost/app/assets/src/js/features/lcp/{lcp.module.scss => status/status.module.scss} (67%)
create mode 100644 projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.tsx
index b2d96f9fbeac7..4374926ba73cd 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.tsx
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.tsx
@@ -1,62 +1,12 @@
-import { __ } from '@wordpress/i18n';
-import styles from './lcp.module.scss';
-import { Button } from '@automattic/jetpack-components';
-import RefreshIcon from '$svg/refresh';
import Module from '$features/module/module';
-import { useLcpState, useOptimizeLcpAction } from './lib/stores/lcp-state';
-import TimeAgo from '$features/critical-css/time-ago/time-ago';
-import { recordBoostEvent } from '$lib/utils/analytics';
import Pill from '$features/ui/pill/pill';
-
-const Status = () => {
- const [ query ] = useLcpState();
- const lcpState = query?.data;
-
- if ( lcpState?.status === 'error' ) {
- return (
-
- { __(
- "An error occurred while optimizing your Cornerstone Page's LCP. Please try again.",
- 'jetpack-boost'
- ) }
-
- );
- }
-
- if ( lcpState?.status === 'not_analyzed' ) {
- // This should never happen, but just in case.
- return (
-
- { __(
- "Click the optimize button to start optimizing your Cornerstone Page's LCP.",
- 'jetpack-boost'
- ) }
-
- );
- }
-
- if ( lcpState?.status === 'pending' ) {
- return (
-
- { __(
- "Jetpack Boost is optimizing your Cornerstone Page's LCP for you.",
- 'jetpack-boost'
- ) }
-
- );
- }
-
- if ( lcpState?.status !== 'analyzed' || ! lcpState?.updated ) {
- return null;
- }
-
- return (
-
- { __( 'Last optimized', 'jetpack-boost' ) }{ ' ' }
- .
-
- );
-};
+import { recordBoostEvent } from '$lib/utils/analytics';
+import RefreshIcon from '$svg/refresh';
+import { Button } from '@automattic/jetpack-components';
+import { __ } from '@wordpress/i18n';
+import styles from './status/status.module.scss';
+import { useLcpState, useOptimizeLcpAction } from './lib/stores/lcp-state';
+import Status from './status/status';
const Lcp = () => {
const [ query ] = useLcpState();
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/lib/stores/lcp-state-types.ts b/projects/plugins/boost/app/assets/src/js/features/lcp/lib/stores/lcp-state-types.ts
index 6d21c9ce7e48d..71d2c547ca8c4 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/lib/stores/lcp-state-types.ts
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/lib/stores/lcp-state-types.ts
@@ -1,7 +1,6 @@
-import { JSONSchema } from '$lib/utils/json-types';
import z from 'zod';
-// @TODO: We don't send this back from the API, but it's here for if we do.
+// TODO: Reflect this in Boost Cloud after Beta release, each one should be an Error type.
export const LcpErrorType = z.enum( [
'UrlError',
'HttpError',
@@ -11,10 +10,7 @@ export const LcpErrorType = z.enum( [
] );
export const LcpErrorDetailsSchema = z.object( {
- url: z.coerce.string(),
message: z.coerce.string(),
- meta: z.record( JSONSchema ).catch( {} ),
- type: LcpErrorType,
} );
export const PageSchema = z.object( {
@@ -25,7 +21,6 @@ export const PageSchema = z.object( {
// Status
status: z.enum( [ 'success', 'pending', 'error' ] ).catch( 'pending' ),
// Error details
- // @TODO: We don't send this back from the API, but it's here for if we do.
errors: z.array( LcpErrorDetailsSchema ).optional(),
} );
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.module.scss b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.module.scss
new file mode 100644
index 0000000000000..e676d3d5c8c30
--- /dev/null
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.module.scss
@@ -0,0 +1,52 @@
+@import '$css/main/variables';
+
+$grey: #8e8e8e;
+$black: #101517;
+$white: #fff;
+
+.jb-error-tooltip {
+ background-color: $black;
+ color: $white;
+ padding: 16px 24px;
+ border-radius: 4px;
+ font-size: 14px;
+ width: max-content;
+ max-width: 30rem;
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ transform: translateX( -50% );
+ margin-top: 8px;
+ box-shadow: 0 1px 2px 0 rgba( 0, 0, 0, 0.05 );
+ z-index: 1000;
+
+ hr {
+ border-top: 1px solid $grey;
+ border-bottom: none;
+ border-left: none;
+ border-right: none;
+ margin: 12px 0;
+ }
+
+ &__header {
+ font-size: 1rem;
+ font-weight: 600;
+ }
+
+ &__row {
+ list-style: disc;
+ margin: 8px 0 8px 12px;
+ }
+
+ &__pointer {
+ width: 0;
+ height: 0;
+ border-left: 8px solid transparent;
+ border-right: 8px solid transparent;
+ border-bottom: 8px solid $black;
+ position: absolute;
+ top: -8px;
+ left: 50%;
+ transform: translateX( -50% );
+ }
+}
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.tsx
new file mode 100644
index 0000000000000..7a7cac6546e20
--- /dev/null
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.tsx
@@ -0,0 +1,30 @@
+import { __ } from '@wordpress/i18n';
+import { type FunctionComponent } from 'react';
+import styles from './error-tooltip.module.scss';
+
+interface ErrorTooltipProps {
+ errors: string[];
+}
+
+export const ErrorTooltip: FunctionComponent< ErrorTooltipProps > = ( { errors } ) => {
+ if ( ! errors || errors.length === 0 ) {
+ return null;
+ }
+
+ return (
+
+
+ { __( 'Optimization Details', 'jetpack-boost' ) }
+
+
+
+ { errors.map( ( error, index ) => (
+
+ { error }
+
+ ) ) }
+
+
+
+ );
+};
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.module.scss b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
similarity index 67%
rename from projects/plugins/boost/app/assets/src/js/features/lcp/lcp.module.scss
rename to projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
index c14164007bc01..d2b70b91447b1 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.module.scss
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
@@ -1,5 +1,5 @@
-@import "$css/main/mixins";
-@import "$css/main/variables";
+@import '$css/main/mixins';
+@import '$css/main/variables';
// TODO: This is a copy of the styles from the critical-css/status component.
// We should move this to a shared location.
@@ -16,7 +16,7 @@
margin-bottom: 0;
}
- @include breakpoint(xs) {
+ @include breakpoint( xs ) {
display: block;
}
@@ -25,10 +25,11 @@
flex-grow: 1;
position: relative;
- .successes, .generating {
+ .successes,
+ .generating {
color: $gray_40;
- @include breakpoint(md) {
+ @include breakpoint( md ) {
margin-right: 115px;
}
}
@@ -37,7 +38,7 @@
margin-top: 1em;
color: $gray_100;
- @include breakpoint(xs) {
+ @include breakpoint( xs ) {
margin-bottom: 1em;
}
@@ -52,7 +53,7 @@
button {
- &:global(.components-button.has-icon) {
+ &:global( .components-button.has-icon ) {
min-width: auto;
}
@@ -61,11 +62,16 @@
}
}
- .optimize-button:global(.components-button) {
+ .optimize-button:global( .components-button ) {
&:disabled {
background: transparent;
- opacity: .5;
+ opacity: 0.5;
}
}
}
+
+.error-tooltip-wrapper {
+ position: relative;
+ display: inline-block;
+}
\ No newline at end of file
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
new file mode 100644
index 0000000000000..04c017f7e488e
--- /dev/null
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
@@ -0,0 +1,145 @@
+import { __, _n, sprintf } from '@wordpress/i18n';
+import clsx from 'clsx';
+import InfoIcon from '$svg/info';
+import { createInterpolateElement, useState } from '@wordpress/element';
+import styles from './status.module.scss';
+import { useLcpState } from '../lib/stores/lcp-state';
+import TimeAgo from '$features/critical-css/time-ago/time-ago';
+import { Button } from '@automattic/jetpack-components';
+import { ErrorTooltip } from './error-tooltip';
+
+const ErrorDetails = () => {
+ const [ query ] = useLcpState();
+ const lcpState = query?.data;
+
+ if ( lcpState?.status !== 'analyzed' ) {
+ return null;
+ }
+
+ const pages = lcpState?.pages;
+ if ( ! pages || pages.length === 0 ) {
+ return null;
+ }
+
+ const errors = pages.filter( page => ( page?.errors?.length || 0 ) > 0 );
+ if ( errors.length === 0 ) {
+ return null;
+ }
+
+ const errorMessages = errors.flatMap( p => p.errors );
+
+ return createInterpolateElement(
+ sprintf(
+ // translators: %d is a number of pages which failed to be optimized
+ _n(
+ '%d page could not be optimized. ',
+ '%d pages could not be optimized. ',
+ errorMessages.length,
+ 'jetpack-boost'
+ ),
+ errorMessages.length
+ ),
+ {
+ errorDetails: ,
+ }
+ );
+};
+
+const ErrorDetailsTooltip = () => {
+ const [ isVisible, setIsVisible ] = useState( false );
+ const [ query ] = useLcpState();
+ const lcpState = query?.data;
+
+ if ( lcpState?.status !== 'analyzed' ) {
+ return null;
+ }
+
+ const pages = lcpState?.pages;
+ if ( ! pages || pages.length === 0 ) {
+ return null;
+ }
+
+ const errors = pages.filter( page => ( page?.errors?.length || 0 ) > 0 );
+ if ( errors.length === 0 ) {
+ return null;
+ }
+
+ const errorMessages = errors.flatMap( p => ( p.errors || [] ).map( e => e.message ) );
+
+ return (
+
+ setIsVisible( ! isVisible ) }
+ aria-expanded={ isVisible }
+ >
+ { __( 'View details', 'jetpack-boost' ) }
+
+ { isVisible && }
+
+ );
+};
+
+const Status: React.FC = () => {
+ const [ query ] = useLcpState();
+ const lcpState = query?.data;
+
+ if ( lcpState?.status === 'error' ) {
+ return (
+
+ { __(
+ "An error occurred while optimizing your Cornerstone Page's LCP. Please try again.",
+ 'jetpack-boost'
+ ) }
+
+ );
+ }
+
+ if ( lcpState?.status === 'not_analyzed' ) {
+ // This should never happen, but just in case.
+ return (
+
+ { __(
+ "Click the optimize button to start optimizing your Cornerstone Page's LCP.",
+ 'jetpack-boost'
+ ) }
+
+ );
+ }
+
+ if ( lcpState?.status === 'pending' ) {
+ return (
+
+ { __(
+ "Jetpack Boost is optimizing your Cornerstone Page's LCP for you.",
+ 'jetpack-boost'
+ ) }
+
+ );
+ }
+
+ if ( lcpState?.status !== 'analyzed' || ! lcpState?.updated ) {
+ return null;
+ }
+
+ return (
+ <>
+
+ { __( 'Last optimized', 'jetpack-boost' ) }{ ' ' }
+ .
+
+ { lcpState?.status === 'analyzed' && (
+
+
+
+
+
+ ) }
+ >
+ );
+};
+
+export default Status;
diff --git a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-state.php b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-state.php
index 669c48241902a..f353e53fe2987 100644
--- a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-state.php
+++ b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-state.php
@@ -112,6 +112,24 @@ public function set_page_success( $page_key ) {
$page_key,
array(
'status' => self::PAGE_STATES['success'],
+ 'errors' => null,
+ )
+ );
+ }
+
+ /**
+ * Signifies that the page was not optimized for reason(s) in $errors.
+ *
+ * @param string $page_key The page key.
+ * @param array $errors The errors to set for the page.
+ * @return bool|\WP_Error True on success, WP_Error on failure.
+ */
+ public function set_page_errors( $page_key, $errors ) {
+ return $this->update_page_state(
+ $page_key,
+ array(
+ 'status' => self::PAGE_STATES['error'],
+ 'errors' => $errors,
)
);
}
diff --git a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp.php b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp.php
index 622181abc3f7f..feca54c45ee07 100644
--- a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp.php
+++ b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp.php
@@ -161,6 +161,13 @@ public function register_data_sync( $instance ) {
'key' => Schema::as_string(),
'url' => Schema::as_string(),
'status' => Schema::as_string(),
+ 'errors' => Schema::as_array(
+ Schema::as_assoc_array(
+ array(
+ 'message' => Schema::as_string(),
+ )
+ )
+ )->nullable(),
)
)
),
diff --git a/projects/plugins/boost/app/rest-api/endpoints/class-update-lcp.php b/projects/plugins/boost/app/rest-api/endpoints/class-update-lcp.php
index 0636a9a8c8b2e..791645208b291 100644
--- a/projects/plugins/boost/app/rest-api/endpoints/class-update-lcp.php
+++ b/projects/plugins/boost/app/rest-api/endpoints/class-update-lcp.php
@@ -63,12 +63,24 @@ public function response( $request ) {
return $api_successful;
}
- // @TODO: handle bad payload coming from the Cloud.
-
// Update each page.
foreach ( $pages as $entry ) {
- // Mark the page as successfully analyzed as we don't know what to do if mobile fails but desktop succeeds.
- $state->set_page_success( $entry['key'] );
+ if ( $entry['success'] ) {
+ // Mark the page as successfully analyzed as we don't know what to do if mobile fails but desktop succeeds.
+ $state->set_page_success( $entry['key'] );
+ } else {
+ $errors = array();
+ foreach ( $entry['reports'] as $report ) {
+ if ( false === $report['success'] && ! empty( $report['message'] ) ) {
+ $errors[] = array(
+ // @TODO: Add a type and meta here (and the Cloud) after Beta release to further explain the error.
+ 'message' => $report['message'] ?? __( 'An unknown error occurred', 'jetpack-boost' ),
+ );
+ }
+ }
+
+ $state->set_page_errors( $entry['key'], $errors );
+ }
// Store the LCP data for this page.
$storage->store_lcp( $entry['key'], $entry['reports'] );
From 36a9e139950c979aa3dbd048211cf76164d7a800 Mon Sep 17 00:00:00 2001
From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com>
Date: Tue, 27 May 2025 18:13:25 +0100
Subject: [PATCH 02/10] Changelog
---
.../plugins/boost/changelog/add-boost-lcp-error-messaging | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 projects/plugins/boost/changelog/add-boost-lcp-error-messaging
diff --git a/projects/plugins/boost/changelog/add-boost-lcp-error-messaging b/projects/plugins/boost/changelog/add-boost-lcp-error-messaging
new file mode 100644
index 0000000000000..85e2e20674008
--- /dev/null
+++ b/projects/plugins/boost/changelog/add-boost-lcp-error-messaging
@@ -0,0 +1,5 @@
+Significance: patch
+Type: added
+Comment: LCP: Changes to an unreleased feature.
+
+
From d0c0af9f76453a6df9391bab93f81fa71d92247b Mon Sep 17 00:00:00 2001
From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com>
Date: Tue, 27 May 2025 18:39:15 +0100
Subject: [PATCH 03/10] Refactor LCP error handling: Introduce ErrorDetails
component
* Add ErrorDetails and ErrorDetailsTooltip components for improved error display in LCP status.
* Update status.module.scss to reflect changes in error handling structure.
---
.../lcp/status/error-details.module.scss | 57 +++++++++
.../js/features/lcp/status/error-details.tsx | 108 ++++++++++++++++++
.../lcp/status/error-tooltip.module.scss | 52 ---------
.../js/features/lcp/status/error-tooltip.tsx | 30 -----
.../js/features/lcp/status/status.module.scss | 5 -
.../src/js/features/lcp/status/status.tsx | 87 +-------------
.../rest-api/endpoints/class-update-lcp.php | 1 -
7 files changed, 170 insertions(+), 170 deletions(-)
create mode 100644 projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss
create mode 100644 projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx
delete mode 100644 projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.module.scss
delete mode 100644 projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.tsx
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss
new file mode 100644
index 0000000000000..6be51596954f7
--- /dev/null
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss
@@ -0,0 +1,57 @@
+@import '$css/main/variables';
+
+$grey: #8e8e8e;
+$black: #101517;
+$white: #fff;
+
+.error-tooltip-wrapper {
+ position: relative;
+ display: inline-block;
+
+ .jb-error-tooltip {
+ background-color: $black;
+ color: $white;
+ padding: 16px 24px;
+ border-radius: 4px;
+ font-size: 14px;
+ width: max-content;
+ max-width: 30rem;
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ transform: translateX( -50% );
+ margin-top: 8px;
+ box-shadow: 0 1px 2px 0 rgba( 0, 0, 0, 0.05 );
+ z-index: 1000;
+
+ hr {
+ border-top: 1px solid $grey;
+ border-bottom: none;
+ border-left: none;
+ border-right: none;
+ margin: 12px 0;
+ }
+
+ &__header {
+ font-size: 1rem;
+ font-weight: 600;
+ }
+
+ &__row {
+ list-style: disc;
+ margin: 8px 0 8px 12px;
+ }
+
+ &__pointer {
+ width: 0;
+ height: 0;
+ border-left: 8px solid transparent;
+ border-right: 8px solid transparent;
+ border-bottom: 8px solid $black;
+ position: absolute;
+ top: -8px;
+ left: 50%;
+ transform: translateX( -50% );
+ }
+ }
+}
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx
new file mode 100644
index 0000000000000..153011ffad954
--- /dev/null
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx
@@ -0,0 +1,108 @@
+import { Button } from '@automattic/jetpack-components';
+import { createInterpolateElement } from '@wordpress/element';
+import { __, _n, sprintf } from '@wordpress/i18n';
+import { useState, type FunctionComponent } from 'react';
+import { useLcpState } from '../lib/stores/lcp-state';
+import styles from './error-details.module.scss';
+
+interface ErrorTooltipProps {
+ errors: string[];
+}
+
+export const ErrorDetails = () => {
+ const [ query ] = useLcpState();
+ const lcpState = query?.data;
+
+ if ( lcpState?.status !== 'analyzed' ) {
+ return null;
+ }
+
+ const pages = lcpState?.pages;
+ if ( ! pages || pages.length === 0 ) {
+ return null;
+ }
+
+ const errors = pages.filter( page => ( page?.errors?.length || 0 ) > 0 );
+ if ( errors.length === 0 ) {
+ return null;
+ }
+
+ const errorMessages = errors.flatMap( p => p.errors );
+
+ return createInterpolateElement(
+ sprintf(
+ // translators: %d is a number of pages which failed to be optimized
+ _n(
+ '%d page could not be optimized. ',
+ '%d pages could not be optimized. ',
+ errorMessages.length,
+ 'jetpack-boost'
+ ),
+ errorMessages.length
+ ),
+ {
+ errorDetails: ,
+ }
+ );
+};
+
+export const ErrorDetailsTooltip = () => {
+ const [ isVisible, setIsVisible ] = useState( false );
+ const [ query ] = useLcpState();
+ const lcpState = query?.data;
+
+ if ( lcpState?.status !== 'analyzed' ) {
+ return null;
+ }
+
+ const pages = lcpState?.pages;
+ if ( ! pages || pages.length === 0 ) {
+ return null;
+ }
+
+ const errors = pages.filter( page => ( page?.errors?.length || 0 ) > 0 );
+ if ( errors.length === 0 ) {
+ return null;
+ }
+
+ const errorMessages = errors.flatMap( p => ( p.errors || [] ).map( e => e.message ) );
+
+ return (
+
+ setIsVisible( ! isVisible ) }
+ aria-expanded={ isVisible }
+ >
+ { __( 'View details', 'jetpack-boost' ) }
+
+ { isVisible && }
+
+ );
+};
+
+export const ErrorTooltip: FunctionComponent< ErrorTooltipProps > = ( { errors } ) => {
+ if ( ! errors || errors.length === 0 ) {
+ return null;
+ }
+
+ return (
+
+
+ { __( 'Optimization Details', 'jetpack-boost' ) }
+
+
+
+ { errors.map( ( error, index ) => (
+
+ { error }
+
+ ) ) }
+
+
+
+ );
+};
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.module.scss b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.module.scss
deleted file mode 100644
index e676d3d5c8c30..0000000000000
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.module.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-@import '$css/main/variables';
-
-$grey: #8e8e8e;
-$black: #101517;
-$white: #fff;
-
-.jb-error-tooltip {
- background-color: $black;
- color: $white;
- padding: 16px 24px;
- border-radius: 4px;
- font-size: 14px;
- width: max-content;
- max-width: 30rem;
- position: absolute;
- top: 100%;
- left: 50%;
- transform: translateX( -50% );
- margin-top: 8px;
- box-shadow: 0 1px 2px 0 rgba( 0, 0, 0, 0.05 );
- z-index: 1000;
-
- hr {
- border-top: 1px solid $grey;
- border-bottom: none;
- border-left: none;
- border-right: none;
- margin: 12px 0;
- }
-
- &__header {
- font-size: 1rem;
- font-weight: 600;
- }
-
- &__row {
- list-style: disc;
- margin: 8px 0 8px 12px;
- }
-
- &__pointer {
- width: 0;
- height: 0;
- border-left: 8px solid transparent;
- border-right: 8px solid transparent;
- border-bottom: 8px solid $black;
- position: absolute;
- top: -8px;
- left: 50%;
- transform: translateX( -50% );
- }
-}
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.tsx
deleted file mode 100644
index 7a7cac6546e20..0000000000000
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-tooltip.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { __ } from '@wordpress/i18n';
-import { type FunctionComponent } from 'react';
-import styles from './error-tooltip.module.scss';
-
-interface ErrorTooltipProps {
- errors: string[];
-}
-
-export const ErrorTooltip: FunctionComponent< ErrorTooltipProps > = ( { errors } ) => {
- if ( ! errors || errors.length === 0 ) {
- return null;
- }
-
- return (
-
-
- { __( 'Optimization Details', 'jetpack-boost' ) }
-
-
-
- { errors.map( ( error, index ) => (
-
- { error }
-
- ) ) }
-
-
-
- );
-};
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
index d2b70b91447b1..ff394c604b6bb 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
@@ -70,8 +70,3 @@
}
}
}
-
-.error-tooltip-wrapper {
- position: relative;
- display: inline-block;
-}
\ No newline at end of file
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
index 04c017f7e488e..212c1cae1aecf 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
@@ -1,87 +1,10 @@
-import { __, _n, sprintf } from '@wordpress/i18n';
-import clsx from 'clsx';
+import TimeAgo from '$features/critical-css/time-ago/time-ago';
import InfoIcon from '$svg/info';
-import { createInterpolateElement, useState } from '@wordpress/element';
-import styles from './status.module.scss';
+import { __ } from '@wordpress/i18n';
+import clsx from 'clsx';
import { useLcpState } from '../lib/stores/lcp-state';
-import TimeAgo from '$features/critical-css/time-ago/time-ago';
-import { Button } from '@automattic/jetpack-components';
-import { ErrorTooltip } from './error-tooltip';
-
-const ErrorDetails = () => {
- const [ query ] = useLcpState();
- const lcpState = query?.data;
-
- if ( lcpState?.status !== 'analyzed' ) {
- return null;
- }
-
- const pages = lcpState?.pages;
- if ( ! pages || pages.length === 0 ) {
- return null;
- }
-
- const errors = pages.filter( page => ( page?.errors?.length || 0 ) > 0 );
- if ( errors.length === 0 ) {
- return null;
- }
-
- const errorMessages = errors.flatMap( p => p.errors );
-
- return createInterpolateElement(
- sprintf(
- // translators: %d is a number of pages which failed to be optimized
- _n(
- '%d page could not be optimized. ',
- '%d pages could not be optimized. ',
- errorMessages.length,
- 'jetpack-boost'
- ),
- errorMessages.length
- ),
- {
- errorDetails: ,
- }
- );
-};
-
-const ErrorDetailsTooltip = () => {
- const [ isVisible, setIsVisible ] = useState( false );
- const [ query ] = useLcpState();
- const lcpState = query?.data;
-
- if ( lcpState?.status !== 'analyzed' ) {
- return null;
- }
-
- const pages = lcpState?.pages;
- if ( ! pages || pages.length === 0 ) {
- return null;
- }
-
- const errors = pages.filter( page => ( page?.errors?.length || 0 ) > 0 );
- if ( errors.length === 0 ) {
- return null;
- }
-
- const errorMessages = errors.flatMap( p => ( p.errors || [] ).map( e => e.message ) );
-
- return (
-
- setIsVisible( ! isVisible ) }
- aria-expanded={ isVisible }
- >
- { __( 'View details', 'jetpack-boost' ) }
-
- { isVisible && }
-
- );
-};
+import { ErrorDetails } from './error-details';
+import styles from './status.module.scss';
const Status: React.FC = () => {
const [ query ] = useLcpState();
diff --git a/projects/plugins/boost/app/rest-api/endpoints/class-update-lcp.php b/projects/plugins/boost/app/rest-api/endpoints/class-update-lcp.php
index 791645208b291..471796de1df42 100644
--- a/projects/plugins/boost/app/rest-api/endpoints/class-update-lcp.php
+++ b/projects/plugins/boost/app/rest-api/endpoints/class-update-lcp.php
@@ -66,7 +66,6 @@ public function response( $request ) {
// Update each page.
foreach ( $pages as $entry ) {
if ( $entry['success'] ) {
- // Mark the page as successfully analyzed as we don't know what to do if mobile fails but desktop succeeds.
$state->set_page_success( $entry['key'] );
} else {
$errors = array();
From 7521e688c671946296f72aaff1e227c585ef9a60 Mon Sep 17 00:00:00 2001
From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com>
Date: Tue, 27 May 2025 18:57:48 +0100
Subject: [PATCH 04/10] Enhance LCP background image optimization: Add
conditional optimization checks
* Introduce checks to determine if background images can be optimized before processing.
* Refactor logic to skip elements that do not meet optimization criteria.
---
.../lcp/class-lcp-optimize-bg-image.php | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php
index bd7d083db3940..3a9331027469b 100644
--- a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php
+++ b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php
@@ -41,6 +41,11 @@ public function preload_background_images() {
$selectors = array();
foreach ( $this->lcp_data as $lcp_data ) {
+ $lcp_optimizer = new LCP_Optimization_Util( $lcp_data );
+ if ( ! $lcp_optimizer->can_optimize() ) {
+ continue;
+ }
+
if ( in_array( $lcp_data['element'], $selectors, true ) ) {
// If we already printed the styling for this element, skip it.
continue;
@@ -74,14 +79,18 @@ public function add_bg_style_override() {
$selectors = array();
foreach ( $this->lcp_data as $lcp_data ) {
+ $lcp_optimizer = new LCP_Optimization_Util( $lcp_data );
+ if ( ! $lcp_optimizer->can_optimize() ) {
+ continue;
+ }
+
if ( in_array( $lcp_data['element'], $selectors, true ) ) {
// If we already printed the styling for this element, skip it.
continue;
}
$selectors[] = $lcp_data['element'];
- $lcp_optimizer = new LCP_Optimization_Util( $lcp_data );
- $image_url = $lcp_optimizer->get_image_to_preload();
+ $image_url = $lcp_optimizer->get_image_to_preload();
if ( empty( $image_url ) ) {
continue;
}
From 7822175bb76cd3381f5522a7f918c9c022209a06 Mon Sep 17 00:00:00 2001
From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com>
Date: Wed, 28 May 2025 10:17:34 +0100
Subject: [PATCH 05/10] Refactor LCP error display: Update ErrorDetails
component and styles
* Enhance the ErrorDetails component to include a summary wrapper for better layout.
* Refactor SCSS styles for error tooltips and summary display, improving visual consistency.
* Simplify the rendering logic in the Status component by directly integrating ErrorDetails.
---
.../lcp/status/error-details.module.scss | 101 ++++++++++--------
.../js/features/lcp/status/error-details.tsx | 32 +++---
.../js/features/lcp/status/status.module.scss | 16 ---
.../src/js/features/lcp/status/status.tsx | 10 +-
4 files changed, 78 insertions(+), 81 deletions(-)
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss
index 6be51596954f7..f5c5556f82978 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss
@@ -1,57 +1,74 @@
+@import '$css/main/mixins';
@import '$css/main/variables';
$grey: #8e8e8e;
$black: #101517;
$white: #fff;
-.error-tooltip-wrapper {
- position: relative;
- display: inline-block;
-
- .jb-error-tooltip {
- background-color: $black;
- color: $white;
- padding: 16px 24px;
- border-radius: 4px;
- font-size: 14px;
- width: max-content;
- max-width: 30rem;
- position: absolute;
- top: 100%;
- left: 50%;
- transform: translateX( -50% );
- margin-top: 8px;
- box-shadow: 0 1px 2px 0 rgba( 0, 0, 0, 0.05 );
- z-index: 1000;
-
- hr {
- border-top: 1px solid $grey;
- border-bottom: none;
- border-left: none;
- border-right: none;
- margin: 12px 0;
- }
+.summary {
+ margin-top: 1em;
+ color: $gray_100;
- &__header {
- font-size: 1rem;
- font-weight: 600;
- }
+ @include breakpoint( xs ) {
+ margin-bottom: 1em;
+ }
- &__row {
- list-style: disc;
- margin: 8px 0 8px 12px;
- }
+ svg {
+ position: absolute;
+ width: 1.4rem;
+ height: 1.4rem;
+ left: -60px;
+ }
+
+ .error-tooltip-wrapper {
+ position: relative;
+ display: inline-block;
- &__pointer {
- width: 0;
- height: 0;
- border-left: 8px solid transparent;
- border-right: 8px solid transparent;
- border-bottom: 8px solid $black;
+ .jb-error-tooltip {
+ background-color: $black;
+ color: $white;
+ padding: 16px 24px;
+ border-radius: 4px;
+ font-size: 14px;
+ width: max-content;
+ max-width: 30rem;
position: absolute;
- top: -8px;
+ top: 100%;
left: 50%;
transform: translateX( -50% );
+ margin-top: 8px;
+ box-shadow: 0 1px 2px 0 rgba( 0, 0, 0, 0.05 );
+ z-index: 1000;
+
+ hr {
+ border-top: 1px solid $grey;
+ border-bottom: none;
+ border-left: none;
+ border-right: none;
+ margin: 12px 0;
+ }
+
+ &__header {
+ font-size: 1rem;
+ font-weight: 600;
+ }
+
+ &__row {
+ list-style: disc;
+ margin: 8px 0 8px 12px;
+ }
+
+ &__pointer {
+ width: 0;
+ height: 0;
+ border-left: 8px solid transparent;
+ border-right: 8px solid transparent;
+ border-bottom: 8px solid $black;
+ position: absolute;
+ top: -8px;
+ left: 50%;
+ transform: translateX( -50% );
+ }
}
}
}
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx
index 153011ffad954..3112abbfc524a 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx
@@ -29,20 +29,24 @@ export const ErrorDetails = () => {
const errorMessages = errors.flatMap( p => p.errors );
- return createInterpolateElement(
- sprintf(
- // translators: %d is a number of pages which failed to be optimized
- _n(
- '%d page could not be optimized. ',
- '%d pages could not be optimized. ',
- errorMessages.length,
- 'jetpack-boost'
- ),
- errorMessages.length
- ),
- {
- errorDetails: ,
- }
+ return (
+
+ { createInterpolateElement(
+ sprintf(
+ // translators: %d is a number of pages which failed to be optimized
+ _n(
+ '%d page could not be optimized. ',
+ '%d pages could not be optimized. ',
+ errorMessages.length,
+ 'jetpack-boost'
+ ),
+ errorMessages.length
+ ),
+ {
+ errorDetails: ,
+ }
+ ) }
+
);
};
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
index ff394c604b6bb..931e8bbbc85a2 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
@@ -33,22 +33,6 @@
margin-right: 115px;
}
}
-
- .failures {
- margin-top: 1em;
- color: $gray_100;
-
- @include breakpoint( xs ) {
- margin-bottom: 1em;
- }
-
- svg {
- position: absolute;
- width: 1.4rem;
- height: 1.4rem;
- left: -60px;
- }
- }
}
button {
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
index 212c1cae1aecf..ae90d2ad87360 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
@@ -1,7 +1,5 @@
import TimeAgo from '$features/critical-css/time-ago/time-ago';
-import InfoIcon from '$svg/info';
import { __ } from '@wordpress/i18n';
-import clsx from 'clsx';
import { useLcpState } from '../lib/stores/lcp-state';
import { ErrorDetails } from './error-details';
import styles from './status.module.scss';
@@ -54,13 +52,7 @@ const Status: React.FC = () => {
{ __( 'Last optimized', 'jetpack-boost' ) }{ ' ' }
.
- { lcpState?.status === 'analyzed' && (
-
-
-
-
-
- ) }
+ { lcpState?.status === 'analyzed' && }
>
);
};
From 4c28ed332b07696700704f5bf3e3a1f4b178faa6 Mon Sep 17 00:00:00 2001
From: Adnan Haque
Date: Wed, 28 May 2025 16:09:21 -0400
Subject: [PATCH 06/10] Use property instead of
---
.../optimizations/lcp/class-lcp-optimization-util.php | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php
index eca9b81d46df6..ff75f09c55fca 100644
--- a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php
+++ b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php
@@ -102,19 +102,15 @@ public function get_image_to_preload() {
return null;
}
- if ( empty( $this->lcp_data['elementData'] ) || ! is_array( $this->lcp_data['elementData'] ) ) {
+ if ( empty( $this->lcp_data['url'] ) ) {
return null;
}
- if ( empty( $this->lcp_data['elementData']['url'] ) ) {
+ if ( ! wp_http_validate_url( $this->lcp_data['url'] ) ) {
return null;
}
- if ( ! wp_http_validate_url( $this->lcp_data['elementData']['url'] ) ) {
- return null;
- }
-
- return $this->lcp_data['elementData']['url'];
+ return $this->lcp_data['url'];
}
/**
From 6c6921797d0c19f4cfe2991148715fbb44906f83 Mon Sep 17 00:00:00 2001
From: Adnan Haque
Date: Wed, 28 May 2025 16:11:34 -0400
Subject: [PATCH 07/10] Rename lcp image URL method and make it reusable
---
.../optimizations/lcp/class-lcp-optimization-util.php | 4 ++--
.../optimizations/lcp/class-lcp-optimize-bg-image.php | 6 +++---
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php
index ff75f09c55fca..4ee676a31e94d 100644
--- a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php
+++ b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php
@@ -93,12 +93,12 @@ public static function should_skip_optimization() {
return false;
}
- public function get_image_to_preload() {
+ public function get_lcp_image_url() {
if ( ! $this->can_optimize() ) {
return null;
}
- if ( LCP::TYPE_BACKGROUND_IMAGE !== $this->lcp_data['type'] ) {
+ if ( LCP::TYPE_BACKGROUND_IMAGE !== $this->lcp_data['type'] && LCP::TYPE_IMAGE !== $this->lcp_data['type'] ) {
return null;
}
diff --git a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php
index bd7d083db3940..4b9a948171c6e 100644
--- a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php
+++ b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php
@@ -81,7 +81,7 @@ public function add_bg_style_override() {
$selectors[] = $lcp_data['element'];
$lcp_optimizer = new LCP_Optimization_Util( $lcp_data );
- $image_url = $lcp_optimizer->get_image_to_preload();
+ $image_url = $lcp_optimizer->get_lcp_image_url();
if ( empty( $image_url ) ) {
continue;
}
@@ -118,12 +118,12 @@ public function add_bg_style_override() {
}
private function get_responsive_image_rules( $lcp_data ) {
- if ( empty( $lcp_data['breakpoints'] ) ) {
+ if ( $lcp_data['type'] !== LCP::TYPE_BACKGROUND_IMAGE || empty( $lcp_data['breakpoints'] ) ) {
return array();
}
$lcp_optimizer = new LCP_Optimization_Util( $lcp_data );
- $image_url = $lcp_optimizer->get_image_to_preload();
+ $image_url = $lcp_optimizer->get_lcp_image_url();
if ( empty( $image_url ) ) {
return array();
From c7a8b107d584bb1c559e2f839eb823bfcf2686f4 Mon Sep 17 00:00:00 2001
From: Adnan Haque
Date: Wed, 28 May 2025 16:21:58 -0400
Subject: [PATCH 08/10] changelog
---
projects/plugins/boost/changelog/update-lcp-bg-url-property | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 projects/plugins/boost/changelog/update-lcp-bg-url-property
diff --git a/projects/plugins/boost/changelog/update-lcp-bg-url-property b/projects/plugins/boost/changelog/update-lcp-bg-url-property
new file mode 100644
index 0000000000000..5b0bbf6c6cbbc
--- /dev/null
+++ b/projects/plugins/boost/changelog/update-lcp-bg-url-property
@@ -0,0 +1,5 @@
+Significance: patch
+Type: changed
+Comment: Unreleased feature
+
+
From 5531637e74dc391a20114ad64c2cc2b2d42b69f7 Mon Sep 17 00:00:00 2001
From: Liam Sarsfield <43409125+LiamSarsfield@users.noreply.github.com>
Date: Thu, 29 May 2025 13:09:29 +0100
Subject: [PATCH 09/10] Refactor LCP error handling: Update ErrorDetails
component and styles
* Integrate FoldingElement for better error message display in the ErrorDetails component.
* Simplify error message rendering and enhance user interaction with expandable details.
* Adjust SCSS styles for improved layout and visual consistency in error summaries.
---
.../folding-element/folding-element.tsx | 8 +-
.../app/assets/src/js/features/lcp/lcp.tsx | 2 +
.../lcp/status/error-details.module.scss | 72 ++----------
.../js/features/lcp/status/error-details.tsx | 111 +++++-------------
.../js/features/lcp/status/status.module.scss | 6 +-
.../src/js/features/lcp/status/status.tsx | 2 -
6 files changed, 49 insertions(+), 152 deletions(-)
diff --git a/projects/plugins/boost/app/assets/src/js/features/critical-css/folding-element/folding-element.tsx b/projects/plugins/boost/app/assets/src/js/features/critical-css/folding-element/folding-element.tsx
index 308f048b56f81..293e0c005044c 100644
--- a/projects/plugins/boost/app/assets/src/js/features/critical-css/folding-element/folding-element.tsx
+++ b/projects/plugins/boost/app/assets/src/js/features/critical-css/folding-element/folding-element.tsx
@@ -1,11 +1,11 @@
-import useMeasure from 'react-use-measure';
+import ChevronDown from '$svg/chevron-down';
+import ChevronUp from '$svg/chevron-up';
+import { Button } from '@automattic/jetpack-components';
import { animated, useSpring } from '@react-spring/web';
import clsx from 'clsx';
import { useState } from 'react';
-import ChevronDown from '$svg/chevron-down';
-import ChevronUp from '$svg/chevron-up';
+import useMeasure from 'react-use-measure';
import styles from './folding-element.module.scss';
-import { Button } from '@automattic/jetpack-components';
type PropTypes = {
labelExpandedText: string;
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.tsx
index 4374926ba73cd..a0b4a54b83782 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.tsx
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/lcp.tsx
@@ -7,6 +7,7 @@ import { __ } from '@wordpress/i18n';
import styles from './status/status.module.scss';
import { useLcpState, useOptimizeLcpAction } from './lib/stores/lcp-state';
import Status from './status/status';
+import { ErrorDetails } from './status/error-details';
const Lcp = () => {
const [ query ] = useLcpState();
@@ -59,6 +60,7 @@ const Lcp = () => {
{ __( 'Optimize', 'jetpack-boost' ) }
+ { lcpState?.status === 'analyzed' && }
);
};
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss
index f5c5556f82978..30d42e39112f2 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.module.scss
@@ -1,74 +1,22 @@
@import '$css/main/mixins';
-@import '$css/main/variables';
-
-$grey: #8e8e8e;
-$black: #101517;
-$white: #fff;
.summary {
- margin-top: 1em;
- color: $gray_100;
+ margin-top: 16px;
+ margin-bottom: 16px;
@include breakpoint( xs ) {
margin-bottom: 1em;
}
- svg {
- position: absolute;
- width: 1.4rem;
- height: 1.4rem;
- left: -60px;
+ &__list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
}
- .error-tooltip-wrapper {
- position: relative;
- display: inline-block;
-
- .jb-error-tooltip {
- background-color: $black;
- color: $white;
- padding: 16px 24px;
- border-radius: 4px;
- font-size: 14px;
- width: max-content;
- max-width: 30rem;
- position: absolute;
- top: 100%;
- left: 50%;
- transform: translateX( -50% );
- margin-top: 8px;
- box-shadow: 0 1px 2px 0 rgba( 0, 0, 0, 0.05 );
- z-index: 1000;
-
- hr {
- border-top: 1px solid $grey;
- border-bottom: none;
- border-left: none;
- border-right: none;
- margin: 12px 0;
- }
-
- &__header {
- font-size: 1rem;
- font-weight: 600;
- }
-
- &__row {
- list-style: disc;
- margin: 8px 0 8px 12px;
- }
-
- &__pointer {
- width: 0;
- height: 0;
- border-left: 8px solid transparent;
- border-right: 8px solid transparent;
- border-bottom: 8px solid $black;
- position: absolute;
- top: -8px;
- left: 50%;
- transform: translateX( -50% );
- }
- }
+ &__row {
+ list-style: disc;
+ margin-bottom: 0;
+ margin-left: 20px;
}
}
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx
index 3112abbfc524a..a751535f212aa 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/error-details.tsx
@@ -1,14 +1,10 @@
-import { Button } from '@automattic/jetpack-components';
-import { createInterpolateElement } from '@wordpress/element';
+import FoldingElement from '$features/critical-css/folding-element/folding-element';
+import { recordBoostEvent } from '$lib/utils/analytics';
+import { Notice } from '@automattic/jetpack-components';
import { __, _n, sprintf } from '@wordpress/i18n';
-import { useState, type FunctionComponent } from 'react';
import { useLcpState } from '../lib/stores/lcp-state';
import styles from './error-details.module.scss';
-interface ErrorTooltipProps {
- errors: string[];
-}
-
export const ErrorDetails = () => {
const [ query ] = useLcpState();
const lcpState = query?.data;
@@ -27,86 +23,43 @@ export const ErrorDetails = () => {
return null;
}
- const errorMessages = errors.flatMap( p => p.errors );
+ const errorMessages = errors.flatMap( p => ( p.errors || [] ).map( e => e.message ) );
return (
-
- { createInterpolateElement(
- sprintf(
+
+
+ { sprintf(
// translators: %d is a number of pages which failed to be optimized
_n(
- '%d page could not be optimized. ',
- '%d pages could not be optimized. ',
+ '%d page could not be optimized.',
+ '%d pages could not be optimized.',
errorMessages.length,
'jetpack-boost'
),
errorMessages.length
- ),
- {
- errorDetails: ,
- }
- ) }
-
- );
-};
-
-export const ErrorDetailsTooltip = () => {
- const [ isVisible, setIsVisible ] = useState( false );
- const [ query ] = useLcpState();
- const lcpState = query?.data;
-
- if ( lcpState?.status !== 'analyzed' ) {
- return null;
- }
-
- const pages = lcpState?.pages;
- if ( ! pages || pages.length === 0 ) {
- return null;
- }
-
- const errors = pages.filter( page => ( page?.errors?.length || 0 ) > 0 );
- if ( errors.length === 0 ) {
- return null;
- }
-
- const errorMessages = errors.flatMap( p => ( p.errors || [] ).map( e => e.message ) );
-
- return (
-
- setIsVisible( ! isVisible ) }
- aria-expanded={ isVisible }
- >
- { __( 'View details', 'jetpack-boost' ) }
-
- { isVisible && }
-
- );
-};
-
-export const ErrorTooltip: FunctionComponent< ErrorTooltipProps > = ( { errors } ) => {
- if ( ! errors || errors.length === 0 ) {
- return null;
- }
-
- return (
-
-
- { __( 'Optimization Details', 'jetpack-boost' ) }
+ ) }
-
-
- { errors.map( ( error, index ) => (
-
- { error }
-
- ) ) }
-
-
-
+ {
+ if ( isExpanded ) {
+ recordBoostEvent( 'lcp_error_details_expanded', {} );
+ }
+ } }
+ >
+
+ { errorMessages.map( ( error, index ) => (
+
+ { error }
+
+ ) ) }
+
+
+
);
};
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
index 931e8bbbc85a2..c923bdb017500 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.module.scss
@@ -5,7 +5,7 @@
// We should move this to a shared location.
.status {
- margin-bottom: 32px;
+ margin-bottom: 16px;
font-size: 14px;
line-height: 22px;
display: flex;
@@ -40,10 +40,6 @@
&:global( .components-button.has-icon ) {
min-width: auto;
}
-
- svg {
- fill: $jetpack-green;
- }
}
.optimize-button:global( .components-button ) {
diff --git a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
index ae90d2ad87360..d0a2d989dd05a 100644
--- a/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
+++ b/projects/plugins/boost/app/assets/src/js/features/lcp/status/status.tsx
@@ -1,7 +1,6 @@
import TimeAgo from '$features/critical-css/time-ago/time-ago';
import { __ } from '@wordpress/i18n';
import { useLcpState } from '../lib/stores/lcp-state';
-import { ErrorDetails } from './error-details';
import styles from './status.module.scss';
const Status: React.FC = () => {
@@ -52,7 +51,6 @@ const Status: React.FC = () => {
{ __( 'Last optimized', 'jetpack-boost' ) }{ ' ' }
.
- { lcpState?.status === 'analyzed' && }
>
);
};
From 1c3e4ee4f442cf8abd365b8f4daa88d7b4849d13 Mon Sep 17 00:00:00 2001
From: Adnan Haque
Date: Thu, 29 May 2025 11:22:15 -0400
Subject: [PATCH 10/10] Use `selector` instead of `element` property
---
.../optimizations/lcp/class-lcp-optimization-util.php | 2 +-
.../optimizations/lcp/class-lcp-optimize-bg-image.php | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php
index 4ee676a31e94d..403bcc86aae0d 100644
--- a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php
+++ b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimization-util.php
@@ -110,7 +110,7 @@ public function get_lcp_image_url() {
return null;
}
- return $this->lcp_data['url'];
+ return $this->lcp_data['url'];
}
/**
diff --git a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php
index 4b9a948171c6e..cb7f5087aaafa 100644
--- a/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php
+++ b/projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php
@@ -41,11 +41,11 @@ public function preload_background_images() {
$selectors = array();
foreach ( $this->lcp_data as $lcp_data ) {
- if ( in_array( $lcp_data['element'], $selectors, true ) ) {
+ if ( in_array( $lcp_data['selector'], $selectors, true ) ) {
// If we already printed the styling for this element, skip it.
continue;
}
- $selectors[] = $lcp_data['element'];
+ $selectors[] = $lcp_data['selector'];
$responsive_image_rules = $this->get_responsive_image_rules( $lcp_data );
$this->print_preload_links( $responsive_image_rules );
@@ -74,11 +74,11 @@ public function add_bg_style_override() {
$selectors = array();
foreach ( $this->lcp_data as $lcp_data ) {
- if ( in_array( $lcp_data['element'], $selectors, true ) ) {
+ if ( in_array( $lcp_data['selector'], $selectors, true ) ) {
// If we already printed the styling for this element, skip it.
continue;
}
- $selectors[] = $lcp_data['element'];
+ $selectors[] = $lcp_data['selector'];
$lcp_optimizer = new LCP_Optimization_Util( $lcp_data );
$image_url = $lcp_optimizer->get_lcp_image_url();
@@ -101,7 +101,7 @@ public function add_bg_style_override() {
$styles[] = sprintf(
'@media %1$s { %2$s { background-image: url(%3$s) !important; background-image: -webkit-image-set(%4$s) !important; background-image: image-set(%4$s) !important; } }',
$breakpoint['media_query'],
- $lcp_data['element'],
+ $lcp_data['selector'],
$breakpoint['base_image'],
$image_set_string
);