• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / 15936d2b846c322fa3b4b91e32ba3f7c002a45a6

13 May 2025 08:35AM UTC coverage: 58.702% (+0.06%) from 58.644%
15936d2b846c322fa3b4b91e32ba3f7c002a45a6

Pull #22258

github

web-flow
Merge pull request #22254 from Yoast/fix/ai-optimize-woo-upsell

Adds an upsell for AI Optimize on products
Pull Request #22258: Merges the feature branch `feature/ai-optimize-classic` to `trunk`

8175 of 14231 branches covered (57.45%)

Branch coverage included in aggregate %.

37 of 68 new or added lines in 11 files covered. (54.41%)

3 existing lines in 2 files now uncovered.

14070 of 23664 relevant lines covered (59.46%)

100886.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/packages/js/src/components/contentAnalysis/ReadabilityAnalysis.js
1
/* global wpseoAdminL10n */
2
/* External components */
3
import { Component, Fragment } from "@wordpress/element";
4
import { withSelect } from "@wordpress/data";
5
import PropTypes from "prop-types";
6
import styled from "styled-components";
7
import { __, sprintf } from "@wordpress/i18n";
8
import { isNil } from "lodash";
9

10
/* Internal components */
11
import ScoreIconPortal from "../portals/ScoreIconPortal";
12
import Results from "../../containers/Results";
13
import Collapsible from "../SidebarCollapsible";
14
import getIndicatorForScore from "../../analysis/getIndicatorForScore";
15
import { getIconForScore } from "./mapResults";
16
import { LocationConsumer, RootContext } from "@yoast/externals/contexts";
17
import HelpLink from "../HelpLink";
18
import ReadabilityResultsPortal from "../portals/ReadabilityResultsPortal";
19
import { isWordComplexitySupported } from "../../helpers/assessmentUpsellHelpers";
20
import { addQueryArgs } from "@wordpress/url";
21
import getL10nObject from "../../analysis/getL10nObject";
22
import AIOptimizeButton from "../../ai-optimizer/components/ai-optimize-button";
23
import { shouldRenderAIOptimizeButton } from "../../helpers/shouldRenderAIOptimizeButton";
24

25
const AnalysisHeader = styled.span`
×
26
        font-size: 1em;
27
        font-weight: bold;
28
        margin: 0 0 8px;
29
        display: block;
30
`;
31

32
const ReadabilityResultsTabContainer = styled.div`
×
33
        padding: 16px;
34
`;
35

36
const StyledHelpLink = styled( HelpLink )`
×
37
        margin: -8px 0 -4px 4px;
38
`;
39

40
/**
41
 * Redux container for the readability analysis.
42
 */
43
class ReadabilityAnalysis extends Component {
44
        /**
45
         * Renders the Readability Analysis results.
46
         *
47
         * @param {Array} upsellResults The array of upsell results.
48
         *
49
         * @returns {wp.Element} The Readability Analysis results.
50
         */
51
        renderResults( upsellResults ) {
52
                const highlightingUpsellLink = "shortlinks.upsell.sidebar.highlighting_readability_analysis";
×
53

54
                return (
×
55
                        <Fragment>
56
                                <AnalysisHeader>
57
                                        { __( "Analysis results", "wordpress-seo" ) }
58
                                        <StyledHelpLink
59
                                                href={ wpseoAdminL10n[ "shortlinks.readability_analysis_info" ] }
60
                                                className="dashicons"
61
                                        >
62
                                                <span className="screen-reader-text">
63
                                                        {
64
                                                                /* translators: Hidden accessibility text. */
65
                                                                __( "Learn more about the readability analysis", "wordpress-seo" )
66
                                                        }
67
                                                </span>
68
                                        </StyledHelpLink>
69
                                </AnalysisHeader>
70
                                <Results
71
                                        results={ this.props.results }
72
                                        upsellResults={ upsellResults }
73
                                        marksButtonClassName="yoast-tooltip yoast-tooltip-w"
74
                                        marksButtonStatus={ this.props.marksButtonStatus }
75
                                        highlightingUpsellLink={ highlightingUpsellLink }
76
                                        shouldUpsellHighlighting={ this.props.shouldUpsellHighlighting }
77
                                        renderAIOptimizeButton={ this.renderAIOptimizeButton }
78
                                />
79
                        </Fragment>
80
                );
81
        }
82

83
        /**
84
         * Returns the list of results used to upsell the user to Premium.
85
         *
86
         * @param {string} location                 Where this component is rendered (metabox or sidebar).
87
         * @param {string} locationContext         In which editor this component is rendered.
88
         *
89
         * @returns {Array} The upsell results.
90
         */
91
        getUpsellResults( location, locationContext ) {
92
                let link = wpseoAdminL10n[ "shortlinks.upsell.metabox.word_complexity" ];
×
93
                if ( location === "sidebar" ) {
×
94
                        link = wpseoAdminL10n[ "shortlinks.upsell.sidebar.word_complexity" ];
×
95
                }
96

97
                link = addQueryArgs( link, { context: locationContext } );
×
98

99
                /*
100
                 * We don't show the upsell for Word complexity assessment if it's not supported for the current locale.
101
                 */
102
                if ( ! isWordComplexitySupported() ) {
×
103
                        return [];
×
104
                }
105

106
                const wordComplexityUpsellText = sprintf(
×
107
                        /* Translators: %1$s is a span tag that adds styling to 'Word complexity', %2$s is a closing span tag.
108
                           %3$s is an anchor tag with a link to yoast.com, %4$s is a closing anchor tag.*/
109
                        __(
110
                                "%1$sWord complexity%2$s: Is your vocabulary suited for a larger audience? %3$sYoast SEO Premium will tell you!%4$s",
111
                                "wordpress-seo"
112
                        ),
113
                        "<span style='text-decoration: underline'>",
114
                        "</span>",
115
                        `<a href="${ link }" data-action="load-nfd-ctb" data-ctb-id="f6a84663-465f-4cb5-8ba5-f7a6d72224b2" target="_blank">`,
116
                        "</a>"
117
                );
118

119
                return [
×
120
                        {
121
                                score: 0,
122
                                rating: "upsell",
123
                                hasMarks: false,
124
                                id: "wordComplexity",
125
                                text: wordComplexityUpsellText,
126
                                markerId: "wordComplexity",
127
                        },
128
                ];
129
        }
130

131

132
        /**
133
         * Renders the Yoast AI Optimize button.
134
         * The button is shown when:
135
         * - The assessment can be fixed through Yoast AI Optimize.
136
         * - The AI feature is enabled (for Yoast SEO Premium users; for Free users, the button is shown with an upsell).
137
         * - We are in the block editor.
138
         * - We are not in the Elementor editor, nor in the Elementor in-between screen.
139
         *
140
         * @param {boolean} hasAIFixes Whether the assessment can be fixed through Yoast AI Optimize.
141
         * @param {string} id The assessment ID.
142
         *
143
         * @returns {void|JSX.Element} The AI Optimize button, or nothing if the button should not be shown.
144
         */
NEW
145
        renderAIOptimizeButton = ( hasAIFixes, id ) => {
×
NEW
146
                const { isElementor, isAiFeatureEnabled, isTerm } = this.props;
×
UNCOV
147
                const isPremium = getL10nObject().isPremium;
×
148

149
                // Don't show the button if the AI feature is not enabled for Yoast SEO Premium users.
150
                if ( isPremium && ! isAiFeatureEnabled ) {
×
151
                        return;
×
152
                }
NEW
153
                const shouldRenderAIButton = shouldRenderAIOptimizeButton( hasAIFixes, isElementor, isTerm );
×
154
                // Show the button if the assessment can be fixed through Yoast AI Optimize, and we are not in the Elementor editor,
155
                // WooCommerce Product pages or Taxonomy
NEW
156
                return shouldRenderAIButton && ( <AIOptimizeButton id={ id } isPremium={ isPremium } /> );
×
157
        };
158

159

160
        /**
161
         * Renders the Readability Analysis component.
162
         *
163
         * @returns {wp.Element} The Readability Analysis component.
164
         */
165
        render() {
166
                const score = getIndicatorForScore( this.props.overallScore );
×
167

168
                if ( isNil( this.props.overallScore ) ) {
×
169
                        score.className = "loading";
×
170
                }
171

172
                return (
×
173
                        <LocationConsumer>
174
                                { location => {
175
                                        return (
×
176
                                                <RootContext.Consumer>
177
                                                        { ( { locationContext } ) => {
178
                                                                let upsellResults = [];
×
179
                                                                if ( this.props.shouldUpsell ) {
×
180
                                                                        upsellResults = this.getUpsellResults( location, locationContext );
×
181
                                                                }
182
                                                                if ( location === "sidebar" ) {
×
183
                                                                        return (
×
184
                                                                                <Collapsible
185
                                                                                        title={ __( "Readability analysis", "wordpress-seo" ) }
186
                                                                                        titleScreenReaderText={ score.screenReaderReadabilityText }
187
                                                                                        prefixIcon={ getIconForScore( score.className ) }
188
                                                                                        prefixIconCollapsed={ getIconForScore( score.className ) }
189
                                                                                        id={ `yoast-readability-analysis-collapsible-${ location }` }
190
                                                                                >
191
                                                                                        { this.renderResults( upsellResults ) }
192
                                                                                </Collapsible>
193
                                                                        );
194
                                                                }
195

196
                                                                if ( location === "metabox" ) {
×
197
                                                                        return (
×
198
                                                                                <ReadabilityResultsPortal target="wpseo-metabox-readability-root">
199
                                                                                        <ReadabilityResultsTabContainer>
200
                                                                                                <ScoreIconPortal
201
                                                                                                        target="wpseo-readability-score-icon"
202
                                                                                                        scoreIndicator={ score.className }
203
                                                                                                />
204
                                                                                                { this.renderResults( upsellResults ) }
205
                                                                                        </ReadabilityResultsTabContainer>
206
                                                                                </ReadabilityResultsPortal>
207
                                                                        );
208
                                                                }
209
                                                        } }
210
                                                </RootContext.Consumer>
211
                                        );
212
                                } }
213
                        </LocationConsumer>
214
                );
215
        }
216
}
217

218
ReadabilityAnalysis.propTypes = {
×
219
        results: PropTypes.array.isRequired,
220
        marksButtonStatus: PropTypes.string.isRequired,
221
        overallScore: PropTypes.number,
222
        shouldUpsell: PropTypes.bool,
223
        shouldUpsellHighlighting: PropTypes.bool,
224
        isAiFeatureEnabled: PropTypes.bool,
225
        isElementor: PropTypes.bool,
226
        isTerm: PropTypes.bool,
227
};
228

229
ReadabilityAnalysis.defaultProps = {
×
230
        overallScore: null,
231
        shouldUpsell: false,
232
        shouldUpsellHighlighting: false,
233
        isAiFeatureEnabled: false,
234
        isElementor: false,
235
        isTerm: false,
236
};
237

238
export default withSelect( select => {
239
        const {
240
                getReadabilityResults,
241
                getMarkButtonStatus,
242
                getIsElementorEditor,
243
                getIsAiFeatureEnabled,
244
                getIsTerm,
UNCOV
245
        } = select( "yoast-seo/editor" );
×
246

247
        return {
×
248
                ...getReadabilityResults(),
249
                marksButtonStatus: getMarkButtonStatus(),
250
                isElementor: getIsElementorEditor(),
251
                isAiFeatureEnabled: getIsAiFeatureEnabled(),
252
                isTerm: getIsTerm(),
253
        };
254
} )( ReadabilityAnalysis );
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc