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

Yoast / wordpress-seo / 451d08bb87d917e1ade06595d6ef8c266f9e01f7

10 Jul 2025 08:11AM UTC coverage: 53.882% (+0.001%) from 53.881%
451d08bb87d917e1ade06595d6ef8c266f9e01f7

Pull #22326

github

web-flow
Merge b51d6bcd6 into 17cba4616
Pull Request #22326: Adjusts the popover component

8246 of 14340 branches covered (57.5%)

Branch coverage included in aggregate %.

30485 of 57541 relevant lines covered (52.98%)

41494.72 hits per line

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

83.33
/packages/js/src/components/SEMrushRelatedKeyphrasesModalContent.js
1
import { useCallback, useState } from "@wordpress/element";
2
import { CountrySelector, KeyphrasesTable, UserMessage, PremiumUpsell } from "@yoast/related-keyphrase-suggestions";
3
import { Root } from "@yoast/ui-library";
4
import PropTypes from "prop-types";
5
import { isEmpty } from "lodash";
6

7
/**
8
 * Determines whether the error property is present in the passed response object.
9
 *
10
 * @param {Object} response The response object.
11
 *
12
 * @returns {boolean} Whether or not the error property is present.
13
 */
14
export function hasError( response ) {
15
        if ( response?.code === "invalid_json" || response?.code === "fetch_error" ) {
12✔
16
                return true;
4✔
17
        }
18
        return ! isEmpty( response ) && "error" in response;
8✔
19
}
20

21
/**
22
 * Determines whether the maximum amount of related keyphrases has been reached.
23
 *
24
 * @param {array} relatedKeyphrases The related keyphrases. Can be empty.
25
 *
26
 * @returns {boolean} Whether or not the maximum limit has been reached.
27
 */
28
export function hasMaximumRelatedKeyphrases( relatedKeyphrases ) {
29
        return relatedKeyphrases && relatedKeyphrases.length >= 4;
26✔
30
}
31

32
/**
33
 * Gets a user message variant.
34
 *
35
 * @param {object} props The props to use within the content.
36
 *
37
 * @returns {?string} The user message variant, or null if no message is needed.
38
 */
39
export function getUserMessage( props ) {
40
        const {
41
                requestLimitReached,
42
                isSuccess,
43
                response,
44
                requestHasData,
45
                relatedKeyphrases,
46
        } = props;
32✔
47

48
        if ( requestLimitReached ) {
32✔
49
                return "requestLimitReached";
4✔
50
        }
51

52
        if ( ! isSuccess && hasError( response ) ) {
28✔
53
                return "requestFailed";
4✔
54
        }
55

56
        if ( ! requestHasData ) {
24✔
57
                return "requestEmpty";
4✔
58
        }
59

60
        if ( hasMaximumRelatedKeyphrases( relatedKeyphrases ) ) {
20✔
61
                return "maxRelatedKeyphrases";
2✔
62
        }
63

64
        return null;
18✔
65
}
66

67
/**
68
 * Renders the SEMrush related keyphrases modal content.
69
 *
70
 * @param {Object} props The props to use within the content.
71
 *
72
 * @returns {wp.Element} The SEMrush related keyphrases modal content.
73
 */
74
export default function RelatedKeyphraseModalContent( props ) {
75
        const {
76
                keyphrase = "",
×
77
                relatedKeyphrases = [],
×
78
                renderAction = null,
×
79
                requestLimitReached = false,
×
80
                countryCode = "us",
×
81
                setCountry,
82
                newRequest,
83
                response = {},
×
84
                isRtl = false,
12✔
85
                userLocale = "en_US",
12✔
86
                isPending = false,
×
87
                isPremium = false,
×
88
                semrushUpsellLink = "",
11✔
89
                premiumUpsellLink = "",
11✔
90
        } = props;
24✔
91

92
        const [ activeCountryCode, setActiveCountryCode ] = useState( countryCode );
24✔
93

94
        /**
95
         * Sends a new related keyphrases request to SEMrush and updates the semrush_country_code value in the database.
96
         *
97
         * @returns {void}
98
         */
99
        const relatedKeyphrasesRequest = useCallback( async() => {
24✔
100
                newRequest( countryCode, keyphrase );
×
101
                setActiveCountryCode( countryCode );
×
102
        }, [ countryCode, keyphrase, newRequest ] );
103

104
        return (
24✔
105
                <Root context={ { isRtl } }>
106

107
                        { ! requestLimitReached && ! isPremium && <PremiumUpsell
24✔
108
                                url={ premiumUpsellLink }
109
                                className="yst-mb-4"
110
                        /> }
111

112
                        { ! requestLimitReached && <CountrySelector
23✔
113
                                countryCode={ countryCode }
114
                                activeCountryCode={ activeCountryCode }
115
                                onChange={ setCountry }
116
                                onClick={ relatedKeyphrasesRequest }
117
                                className="yst-mb-4"
118
                                userLocale={ userLocale.split( "_" )[ 0 ] }
119
                        /> }
120

121
                        { ! isPending && <UserMessage
24✔
122
                                variant={ getUserMessage( props ) }
123
                                upsellLink={ semrushUpsellLink }
124
                        /> }
125

126
                        <KeyphrasesTable
127
                                relatedKeyphrases={ relatedKeyphrases }
128
                                columnNames={ response?.results?.columnNames }
129
                                data={ response?.results?.rows }
130
                                isPending={ isPending }
131
                                renderButton={ renderAction }
132
                                className="yst-mt-4"
133
                        />
134
                </Root>
135
        );
136
}
137

138
RelatedKeyphraseModalContent.propTypes = {
2✔
139
        keyphrase: PropTypes.string,
140
        relatedKeyphrases: PropTypes.array,
141
        renderAction: PropTypes.func,
142
        requestLimitReached: PropTypes.bool,
143
        countryCode: PropTypes.string.isRequired,
144
        setCountry: PropTypes.func.isRequired,
145
        newRequest: PropTypes.func.isRequired,
146
        response: PropTypes.object,
147
        isRtl: PropTypes.bool,
148
        userLocale: PropTypes.string,
149
        isPending: PropTypes.bool,
150
        isPremium: PropTypes.bool,
151
        semrushUpsellLink: PropTypes.string,
152
        premiumUpsellLink: PropTypes.string,
153
};
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

© 2026 Coveralls, Inc