Skip to content

Commit 3224cfd

Browse files
Boost: Surface Error details for LCP Optimization (#43647)
Co-authored-by: Adnan Haque <[email protected]>
1 parent 006f39f commit 3224cfd

File tree

12 files changed

+225
-102
lines changed

12 files changed

+225
-102
lines changed

projects/plugins/boost/app/assets/src/js/features/critical-css/folding-element/folding-element.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import useMeasure from 'react-use-measure';
1+
import ChevronDown from '$svg/chevron-down';
2+
import ChevronUp from '$svg/chevron-up';
3+
import { Button } from '@automattic/jetpack-components';
24
import { animated, useSpring } from '@react-spring/web';
35
import clsx from 'clsx';
46
import { useState } from 'react';
5-
import ChevronDown from '$svg/chevron-down';
6-
import ChevronUp from '$svg/chevron-up';
7+
import useMeasure from 'react-use-measure';
78
import styles from './folding-element.module.scss';
8-
import { Button } from '@automattic/jetpack-components';
99

1010
type PropTypes = {
1111
labelExpandedText: string;

projects/plugins/boost/app/assets/src/js/features/lcp/lcp.tsx

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,13 @@
1-
import { __ } from '@wordpress/i18n';
2-
import styles from './lcp.module.scss';
3-
import { Button } from '@automattic/jetpack-components';
4-
import RefreshIcon from '$svg/refresh';
51
import Module from '$features/module/module';
6-
import { useLcpState, useOptimizeLcpAction } from './lib/stores/lcp-state';
7-
import TimeAgo from '$features/critical-css/time-ago/time-ago';
8-
import { recordBoostEvent } from '$lib/utils/analytics';
92
import Pill from '$features/ui/pill/pill';
10-
11-
const Status = () => {
12-
const [ query ] = useLcpState();
13-
const lcpState = query?.data;
14-
15-
if ( lcpState?.status === 'error' ) {
16-
return (
17-
<div className={ styles?.failures }>
18-
{ __(
19-
"An error occurred while optimizing your Cornerstone Page's LCP. Please try again.",
20-
'jetpack-boost'
21-
) }
22-
</div>
23-
);
24-
}
25-
26-
if ( lcpState?.status === 'not_analyzed' ) {
27-
// This should never happen, but just in case.
28-
return (
29-
<div>
30-
{ __(
31-
"Click the optimize button to start optimizing your Cornerstone Page's LCP.",
32-
'jetpack-boost'
33-
) }
34-
</div>
35-
);
36-
}
37-
38-
if ( lcpState?.status === 'pending' ) {
39-
return (
40-
<div className={ styles?.generating }>
41-
{ __(
42-
"Jetpack Boost is optimizing your Cornerstone Page's LCP for you.",
43-
'jetpack-boost'
44-
) }
45-
</div>
46-
);
47-
}
48-
49-
if ( lcpState?.status !== 'analyzed' || ! lcpState?.updated ) {
50-
return null;
51-
}
52-
53-
return (
54-
<div className={ styles?.successes }>
55-
{ __( 'Last optimized', 'jetpack-boost' ) }{ ' ' }
56-
<TimeAgo time={ new Date( lcpState.updated * 1000 ) } />.
57-
</div>
58-
);
59-
};
3+
import { recordBoostEvent } from '$lib/utils/analytics';
4+
import RefreshIcon from '$svg/refresh';
5+
import { Button } from '@automattic/jetpack-components';
6+
import { __ } from '@wordpress/i18n';
7+
import styles from './status/status.module.scss';
8+
import { useLcpState, useOptimizeLcpAction } from './lib/stores/lcp-state';
9+
import Status from './status/status';
10+
import { ErrorDetails } from './status/error-details';
6011

6112
const Lcp = () => {
6213
const [ query ] = useLcpState();
@@ -109,6 +60,7 @@ const Lcp = () => {
10960
{ __( 'Optimize', 'jetpack-boost' ) }
11061
</Button>
11162
</div>
63+
{ lcpState?.status === 'analyzed' && <ErrorDetails /> }
11264
</Module>
11365
);
11466
};

projects/plugins/boost/app/assets/src/js/features/lcp/lib/stores/lcp-state-types.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { JSONSchema } from '$lib/utils/json-types';
21
import z from 'zod';
32

4-
// @TODO: We don't send this back from the API, but it's here for if we do.
3+
// TODO: Reflect this in Boost Cloud after Beta release, each one should be an Error type.
54
export const LcpErrorType = z.enum( [
65
'UrlError',
76
'HttpError',
@@ -11,10 +10,7 @@ export const LcpErrorType = z.enum( [
1110
] );
1211

1312
export const LcpErrorDetailsSchema = z.object( {
14-
url: z.coerce.string(),
1513
message: z.coerce.string(),
16-
meta: z.record( JSONSchema ).catch( {} ),
17-
type: LcpErrorType,
1814
} );
1915

2016
export const PageSchema = z.object( {
@@ -25,7 +21,6 @@ export const PageSchema = z.object( {
2521
// Status
2622
status: z.enum( [ 'success', 'pending', 'error' ] ).catch( 'pending' ),
2723
// Error details
28-
// @TODO: We don't send this back from the API, but it's here for if we do.
2924
errors: z.array( LcpErrorDetailsSchema ).optional(),
3025
} );
3126

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@import '$css/main/mixins';
2+
3+
.summary {
4+
margin-top: 16px;
5+
margin-bottom: 16px;
6+
7+
@include breakpoint( xs ) {
8+
margin-bottom: 1em;
9+
}
10+
11+
&__list {
12+
display: flex;
13+
flex-direction: column;
14+
gap: 8px;
15+
}
16+
17+
&__row {
18+
list-style: disc;
19+
margin-bottom: 0;
20+
margin-left: 20px;
21+
}
22+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import FoldingElement from '$features/critical-css/folding-element/folding-element';
2+
import { recordBoostEvent } from '$lib/utils/analytics';
3+
import { Notice } from '@automattic/jetpack-components';
4+
import { __, _n, sprintf } from '@wordpress/i18n';
5+
import { useLcpState } from '../lib/stores/lcp-state';
6+
import styles from './error-details.module.scss';
7+
8+
export const ErrorDetails = () => {
9+
const [ query ] = useLcpState();
10+
const lcpState = query?.data;
11+
12+
if ( lcpState?.status !== 'analyzed' ) {
13+
return null;
14+
}
15+
16+
const pages = lcpState?.pages;
17+
if ( ! pages || pages.length === 0 ) {
18+
return null;
19+
}
20+
21+
const errors = pages.filter( page => ( page?.errors?.length || 0 ) > 0 );
22+
if ( errors.length === 0 ) {
23+
return null;
24+
}
25+
26+
const errorMessages = errors.flatMap( p => ( p.errors || [] ).map( e => e.message ) );
27+
28+
return (
29+
<Notice
30+
level="warning"
31+
hideCloseButton={ true }
32+
title={ __( 'LCP Optimization issues', 'jetpack-boost' ) }
33+
>
34+
<div className={ styles.summary }>
35+
{ sprintf(
36+
// translators: %d is a number of pages which failed to be optimized
37+
_n(
38+
'%d page could not be optimized.',
39+
'%d pages could not be optimized.',
40+
errorMessages.length,
41+
'jetpack-boost'
42+
),
43+
errorMessages.length
44+
) }
45+
</div>
46+
<FoldingElement
47+
labelExpandedText={ __( 'View details', 'jetpack-boost' ) }
48+
labelCollapsedText={ __( 'Hide details', 'jetpack-boost' ) }
49+
onExpand={ ( isExpanded: boolean ) => {
50+
if ( isExpanded ) {
51+
recordBoostEvent( 'lcp_error_details_expanded', {} );
52+
}
53+
} }
54+
>
55+
<ul className={ styles.summary__list }>
56+
{ errorMessages.map( ( error, index ) => (
57+
<li className={ styles.summary__row } key={ index }>
58+
{ error }
59+
</li>
60+
) ) }
61+
</ul>
62+
</FoldingElement>
63+
</Notice>
64+
);
65+
};
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
@import "$css/main/mixins";
2-
@import "$css/main/variables";
1+
@import '$css/main/mixins';
2+
@import '$css/main/variables';
33

44
// TODO: This is a copy of the styles from the critical-css/status component.
55
// We should move this to a shared location.
66

77
.status {
8-
margin-bottom: 32px;
8+
margin-bottom: 16px;
99
font-size: 14px;
1010
line-height: 22px;
1111
display: flex;
@@ -16,7 +16,7 @@
1616
margin-bottom: 0;
1717
}
1818

19-
@include breakpoint(xs) {
19+
@include breakpoint( xs ) {
2020
display: block;
2121
}
2222

@@ -25,47 +25,28 @@
2525
flex-grow: 1;
2626
position: relative;
2727

28-
.successes, .generating {
28+
.successes,
29+
.generating {
2930
color: $gray_40;
3031

31-
@include breakpoint(md) {
32+
@include breakpoint( md ) {
3233
margin-right: 115px;
3334
}
3435
}
35-
36-
.failures {
37-
margin-top: 1em;
38-
color: $gray_100;
39-
40-
@include breakpoint(xs) {
41-
margin-bottom: 1em;
42-
}
43-
44-
svg {
45-
position: absolute;
46-
width: 1.4rem;
47-
height: 1.4rem;
48-
left: -60px;
49-
}
50-
}
5136
}
5237

5338
button {
5439

55-
&:global(.components-button.has-icon) {
40+
&:global( .components-button.has-icon ) {
5641
min-width: auto;
5742
}
58-
59-
svg {
60-
fill: $jetpack-green;
61-
}
6243
}
6344

64-
.optimize-button:global(.components-button) {
45+
.optimize-button:global( .components-button ) {
6546

6647
&:disabled {
6748
background: transparent;
68-
opacity: .5;
49+
opacity: 0.5;
6950
}
7051
}
7152
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import TimeAgo from '$features/critical-css/time-ago/time-ago';
2+
import { __ } from '@wordpress/i18n';
3+
import { useLcpState } from '../lib/stores/lcp-state';
4+
import styles from './status.module.scss';
5+
6+
const Status: React.FC = () => {
7+
const [ query ] = useLcpState();
8+
const lcpState = query?.data;
9+
10+
if ( lcpState?.status === 'error' ) {
11+
return (
12+
<div className={ styles?.failures }>
13+
{ __(
14+
"An error occurred while optimizing your Cornerstone Page's LCP. Please try again.",
15+
'jetpack-boost'
16+
) }
17+
</div>
18+
);
19+
}
20+
21+
if ( lcpState?.status === 'not_analyzed' ) {
22+
// This should never happen, but just in case.
23+
return (
24+
<div>
25+
{ __(
26+
"Click the optimize button to start optimizing your Cornerstone Page's LCP.",
27+
'jetpack-boost'
28+
) }
29+
</div>
30+
);
31+
}
32+
33+
if ( lcpState?.status === 'pending' ) {
34+
return (
35+
<div className={ styles?.generating }>
36+
{ __(
37+
"Jetpack Boost is optimizing your Cornerstone Page's LCP for you.",
38+
'jetpack-boost'
39+
) }
40+
</div>
41+
);
42+
}
43+
44+
if ( lcpState?.status !== 'analyzed' || ! lcpState?.updated ) {
45+
return null;
46+
}
47+
48+
return (
49+
<>
50+
<div className={ styles?.successes }>
51+
{ __( 'Last optimized', 'jetpack-boost' ) }{ ' ' }
52+
<TimeAgo time={ new Date( lcpState.updated * 1000 ) } />.
53+
</div>
54+
</>
55+
);
56+
};
57+
58+
export default Status;

projects/plugins/boost/app/modules/optimizations/lcp/class-lcp-optimize-bg-image.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ public function preload_background_images() {
4141
$selectors = array();
4242

4343
foreach ( $this->lcp_data as $lcp_data ) {
44+
$lcp_optimizer = new LCP_Optimization_Util( $lcp_data );
45+
if ( ! $lcp_optimizer->can_optimize() ) {
46+
continue;
47+
}
48+
4449
if ( in_array( $lcp_data['selector'], $selectors, true ) ) {
4550
// If we already printed the styling for this element, skip it.
4651
continue;
@@ -74,14 +79,18 @@ public function add_bg_style_override() {
7479
$selectors = array();
7580

7681
foreach ( $this->lcp_data as $lcp_data ) {
82+
$lcp_optimizer = new LCP_Optimization_Util( $lcp_data );
83+
if ( ! $lcp_optimizer->can_optimize() ) {
84+
continue;
85+
}
86+
7787
if ( in_array( $lcp_data['selector'], $selectors, true ) ) {
7888
// If we already printed the styling for this element, skip it.
7989
continue;
8090
}
8191
$selectors[] = $lcp_data['selector'];
8292

83-
$lcp_optimizer = new LCP_Optimization_Util( $lcp_data );
84-
$image_url = $lcp_optimizer->get_lcp_image_url();
93+
$image_url = $lcp_optimizer->get_lcp_image_url();
8594
if ( empty( $image_url ) ) {
8695
continue;
8796
}

0 commit comments

Comments
 (0)