From 02cc236670ee5b93aa9e4bd80ec3609bc550a362 Mon Sep 17 00:00:00 2001 From: Martijn van der Klis Date: Thu, 16 Feb 2023 14:18:08 +0100 Subject: [PATCH 01/52] Adds a hook to be able to focus on input fields from other plugins --- .../src/components/contentAnalysis/Results.js | 39 +++++++++++-------- .../seo/ProductIdentifiersAssessment.js | 5 +++ .../assessments/seo/ProductSKUAssessment.js | 5 +++ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/js/src/components/contentAnalysis/Results.js b/packages/js/src/components/contentAnalysis/Results.js index 06934120b3d..9affd81a648 100644 --- a/packages/js/src/components/contentAnalysis/Results.js +++ b/packages/js/src/components/contentAnalysis/Results.js @@ -1,4 +1,5 @@ import { __ } from "@wordpress/i18n"; +import { doAction } from "@wordpress/hooks"; import PropTypes from "prop-types"; import { ContentAnalysis } from "@yoast/analysis-report"; import { Component, Fragment } from "@wordpress/element"; @@ -131,8 +132,7 @@ class Results extends Component { // The keyword key is used for labelling the related keyphrase(s). const keywordKey = this.props.keywordKey; const elementID = keywordKey === "" ? "focus-keyword-input-" + inputFieldLocation - : "yoast-keyword-input-" + keywordKey + "-" + inputFieldLocation; - + : "yoast-keyword-input-" + keywordKey + "-" + inputFieldLocation; const element = document.getElementById( elementID ); element.focus(); @@ -186,29 +186,34 @@ class Results extends Component { this.focusOnKeyphraseField( inputFieldLocation ); return; } + /* * For all the other assessments that have an edit button, we need to jump to the relevant Google preview fields. * (metadescription, slug, or title). If the user is in the sidebar, these are accessed through a modal. So if the * inputFieldLocation string is 'sidebar' it should now be changed to 'modal'. */ - if ( inputFieldLocation === "sidebar" ) { - inputFieldLocation = "modal"; - // Open the modal. - document.getElementById( "yoast-google-preview-modal-open-button" ).click(); - // Wait for the input field elements to become available, then focus on the relevant field. - setTimeout( () => this.focusOnGooglePreviewField( id, inputFieldLocation ), 500 ); - } else { - const googlePreviewCollapsible = document.getElementById( "yoast-snippet-editor-metabox" ); - // Check if the collapsible is closed before clicking on it. - if ( googlePreviewCollapsible && googlePreviewCollapsible.getAttribute( "aria-expanded" ) === "false" ) { - // If it is closed, click on it to open it, and wait a bit before focusing on the edit field. - googlePreviewCollapsible.click(); - setTimeout( () => this.focusOnGooglePreviewField( id, inputFieldLocation ), 100 ); + if ( [ "metaDescriptionKeyword", "metaDescriptionLength", "titleWidth", "keyphraseInSEOTitle", "slugKeyword" ].includes( id ) ) { + if ( inputFieldLocation === "sidebar" ) { + inputFieldLocation = "modal"; + // Open the modal. + document.getElementById( "yoast-google-preview-modal-open-button" ).click(); + // Wait for the input field elements to become available, then focus on the relevant field. + setTimeout( () => this.focusOnGooglePreviewField( id, inputFieldLocation ), 500 ); } else { - // Collapsible already open, we can click on the field directly. - this.focusOnGooglePreviewField( id, inputFieldLocation ); + const googlePreviewCollapsible = document.getElementById( "yoast-snippet-editor-metabox" ); + // Check if the collapsible is closed before clicking on it. + if ( googlePreviewCollapsible && googlePreviewCollapsible.getAttribute( "aria-expanded" ) === "false" ) { + // If it is closed, click on it to open it, and wait a bit before focusing on the edit field. + googlePreviewCollapsible.click(); + setTimeout( () => this.focusOnGooglePreviewField( id, inputFieldLocation ), 100 ); + } else { + // Collapsible already open, we can click on the field directly. + this.focusOnGooglePreviewField( id, inputFieldLocation ); + } } } + + doAction( "yoast.focus.input", id ); } /** diff --git a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js index ebf9f4ba42f..8cb7edeb9ec 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js @@ -53,6 +53,11 @@ export default class ProductIdentifiersAssessment extends Assessment { assessmentResult.setText( result.text ); } + if ( assessmentResult.getScore() < 9 ) { + assessmentResult.setHasJumps( true ); + assessmentResult.setEditFieldName( __( "Product identifiers", "wordpress-seo" ) ); + } + return assessmentResult; } diff --git a/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js index b77c08d74be..3e8861ffcb3 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js @@ -52,6 +52,11 @@ export default class ProductSKUAssessment extends Assessment { assessmentResult.setText( result.text ); } + if ( assessmentResult.getScore() < 9 ) { + assessmentResult.setHasJumps( true ); + assessmentResult.setEditFieldName( __( "SKU", "wordpress-seo" ) ); + } + return assessmentResult; } From 2ba1104b6a870458ae3a18169fa2c4d82326727d Mon Sep 17 00:00:00 2001 From: Mykola Shlyakhtun Date: Tue, 14 Mar 2023 10:28:34 +0200 Subject: [PATCH 02/52] Add actionable button to SKU assessment#19866 Notification message removed. Tests fixed. --- .../seo/ProductIdentifiersAssessmentSpec.js | 8 ++++---- .../assessments/seo/ProductSKUAssessmentSpec.js | 4 ++-- .../fullTextTests/testTexts/en/englishPaper2.js | 4 ++-- .../fullTextTests/testTexts/en/englishPaper3.js | 2 +- .../seo/ProductIdentifiersAssessment.js | 3 +-- .../assessments/seo/ProductSKUAssessment.js | 14 ++------------ 6 files changed, 12 insertions(+), 23 deletions(-) diff --git a/packages/yoastseo/spec/scoring/assessments/seo/ProductIdentifiersAssessmentSpec.js b/packages/yoastseo/spec/scoring/assessments/seo/ProductIdentifiersAssessmentSpec.js index 7917299bbfb..8d606e02131 100644 --- a/packages/yoastseo/spec/scoring/assessments/seo/ProductIdentifiersAssessmentSpec.js +++ b/packages/yoastseo/spec/scoring/assessments/seo/ProductIdentifiersAssessmentSpec.js @@ -103,8 +103,8 @@ describe( "a test for Product identifiers assessment for WooCommerce", function( expect( assessmentResult.getScore() ).toEqual( 6 ); expect( assessmentResult.getText() ).toEqual( "Product identifier:" + - " Not all your product variants have an identifier. You can add a product identifier via the \"Variations\" " + - "tab in the Product data box. Include it if you can, as it " + + " Not all your product variants have an identifier. " + + "Include it if you can, as it " + "will help search engines to better understand your content." ); } ); @@ -121,8 +121,8 @@ describe( "a test for Product identifiers assessment for WooCommerce", function( expect( assessmentResult.getScore() ).toEqual( 6 ); expect( assessmentResult.getText() ).toEqual( "Product identifier:" + - " Not all your product variants have an identifier. You can add a product identifier via the \"Variations\"" + - " tab in the Product data box. Include" + + " Not all your product variants have an identifier." + + " Include" + " it if you can, as it will help search engines to better understand your content." ); } ); diff --git a/packages/yoastseo/spec/scoring/assessments/seo/ProductSKUAssessmentSpec.js b/packages/yoastseo/spec/scoring/assessments/seo/ProductSKUAssessmentSpec.js index 59480daeec4..7a73aa790f6 100644 --- a/packages/yoastseo/spec/scoring/assessments/seo/ProductSKUAssessmentSpec.js +++ b/packages/yoastseo/spec/scoring/assessments/seo/ProductSKUAssessmentSpec.js @@ -80,7 +80,7 @@ describe( "a test for SKU assessment for WooCommerce", function() { expect( assessmentResult.getScore() ).toEqual( 6 ); expect( assessmentResult.getText() ).toEqual( "SKU:" + - " Your product is missing a SKU. You can add a SKU via the \"Inventory\" tab in the Product data box. " + + " Your product is missing a SKU. " + "Include it if you can, as it will help search engines " + "to better understand your content." ); } ); @@ -149,7 +149,7 @@ describe( "a test for SKU assessment for WooCommerce", function() { expect( assessmentResult.getScore() ).toEqual( 6 ); expect( assessmentResult.getText() ).toEqual( "SKU:" + - " Your product is missing a SKU. You can add a SKU via the \"Inventory\" tab in the Product data box. " + + " Your product is missing a SKU. " + "Include it if you can, as it will help search engines to " + "better understand your content." ); } ); diff --git a/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper2.js b/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper2.js index ba938d7ee20..1feb6215684 100644 --- a/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper2.js +++ b/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper2.js @@ -108,7 +108,6 @@ const expectedResults = { isApplicable: true, score: 6, resultText: "SKU: Your product is missing a SKU. " + - "You can add a SKU via the \"Inventory\" tab in the Product data box. " + "Include it if you can, as it will " + "help search engines to better understand your content.", }, @@ -179,7 +178,8 @@ const expectedResults = { wordComplexity: { isApplicable: true, score: 6, - resultText: "Word complexity: 14.71% of the words in your text are considered complex." + + resultText: "Word complexity: 14.71% of the words in your " + + "text are considered complex." + " Try to use shorter and more familiar words to improve readability.", }, }; diff --git a/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper3.js b/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper3.js index 1fa8446d75e..9b8d6b0c912 100644 --- a/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper3.js +++ b/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper3.js @@ -100,7 +100,7 @@ const expectedResults = { isApplicable: true, score: 6, resultText: "Product identifier: Not all your product variants" + - " have an identifier. You can add a product identifier via the \"Variations\" tab in the Product data box. " + + " have an identifier. " + "Include it if you can, as it" + " will help search engines to better understand your content.", }, diff --git a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js index 56d22641ef9..de4529240d3 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js @@ -108,8 +108,7 @@ export default class ProductIdentifiersAssessment extends Assessment { okNoVariants: __( "Your product is missing an identifier (like a GTIN code). " + "You can add a product identifier via the \"Yoast SEO\" tab in the Product data box", "wordpress-seo" ), goodNoVariants: __( "Your product has an identifier", "wordpress-seo" ), - okWithVariants: __( "Not all your product variants have an identifier. " + - "You can add a product identifier via the \"Variations\" tab in the Product data box", "wordpress-seo" ), + okWithVariants: __( "Not all your product variants have an identifier", "wordpress-seo" ), goodWithVariants: __( "All your product variants have an identifier", "wordpress-seo" ), }; } else { diff --git a/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js index 3e8861ffcb3..ac5010d856f 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js @@ -103,14 +103,6 @@ export default class ProductSKUAssessment extends Assessment { * or empty object if no score should be returned. */ scoreProductSKU( productSKUData, config ) { - // Check if we want to add information about where to add the SKU in the feedback string or not. - // Currently we want to implement it only for Woo Product pages. - let feedbackString = ""; - if ( this._config.addSKULocation === true ) { - // Translators: please keep the space at the start of the sentence in your translation unless your language does not use spaces. - feedbackString = __( " You can add a SKU via the \"Inventory\" tab in the Product data box.", "wordpress-seo" ); - } - // Apply the following scoring conditions to products without variants. if ( [ "simple", "external", "grouped" ].includes( productSKUData.productType ) || ( productSKUData.productType === "variable" && ! productSKUData.hasVariants ) ) { @@ -119,16 +111,14 @@ export default class ProductSKUAssessment extends Assessment { score: config.scores.ok, text: sprintf( // Translators: %1$s and %2$s expand to links on yoast.com, %3$s expands to the anchor end tag. - // %4$s expands to "You can add a SKU via the "Inventory" tab in the Product data box." or to an empty string. __( - "%1$sSKU%3$s: Your product is missing a SKU.%4$s" + + "%1$sSKU%3$s: Your product is missing a SKU." + " %2$sInclude it if you can, as it will help search engines to better understand your content.%3$s", "wordpress-seo" ), this._config.urlTitle, this._config.urlCallToAction, - "", - feedbackString + "" ), }; } From 227a08d6f110306a28999d45b6a66d27f035dc66 Mon Sep 17 00:00:00 2001 From: Mykola Shlyakhtun Date: Wed, 15 Mar 2023 10:32:55 +0200 Subject: [PATCH 03/52] Add actionable button to SKU assessment#19866 Remove navigation text. --- .../assessments/seo/ProductIdentifiersAssessmentSpec.js | 8 ++++---- .../fullTextTests/testTexts/en/englishPaper2.js | 2 +- .../assessments/seo/ProductIdentifiersAssessment.js | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/yoastseo/spec/scoring/assessments/seo/ProductIdentifiersAssessmentSpec.js b/packages/yoastseo/spec/scoring/assessments/seo/ProductIdentifiersAssessmentSpec.js index 8d606e02131..984d61549ab 100644 --- a/packages/yoastseo/spec/scoring/assessments/seo/ProductIdentifiersAssessmentSpec.js +++ b/packages/yoastseo/spec/scoring/assessments/seo/ProductIdentifiersAssessmentSpec.js @@ -85,8 +85,8 @@ describe( "a test for Product identifiers assessment for WooCommerce", function( expect( assessmentResult.getScore() ).toEqual( 6 ); expect( assessmentResult.getText() ).toEqual( "Product identifier:" + - " Your product is missing an identifier (like a GTIN code). You can add a product identifier via the \"Yoast SEO\" tab " + - "in the Product data box. Include" + + " Your product is missing an identifier (like a GTIN code)." + + " Include" + " it if you can, as it will help search engines to better understand your content." ); } ); @@ -156,8 +156,8 @@ describe( "a test for Product identifiers assessment for WooCommerce", function( expect( assessmentResult.getScore() ).toEqual( 6 ); expect( assessmentResult.getText() ).toEqual( "Product identifier:" + - " Your product is missing an identifier (like a GTIN code). You can add a product identifier via the \"Yoast SEO\" tab " + - "in the Product data box. Include" + + " Your product is missing an identifier (like a GTIN code)." + + " Include" + " it if you can, as it will help search engines to better understand your content." ); } ); } ); diff --git a/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper2.js b/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper2.js index 1feb6215684..fc71060de76 100644 --- a/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper2.js +++ b/packages/yoastseo/spec/scoring/productPages/fullTextTests/testTexts/en/englishPaper2.js @@ -100,7 +100,7 @@ const expectedResults = { isApplicable: true, score: 6, resultText: "Product identifier: Your product is missing an identifier " + - "(like a GTIN code). You can add a product identifier via the \"Yoast SEO\" tab in the Product data box." + + "(like a GTIN code)." + " Include it if you can, as it will " + "help search engines to better understand your content.", }, diff --git a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js index de4529240d3..b6560325ec8 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js @@ -105,8 +105,7 @@ export default class ProductIdentifiersAssessment extends Assessment { if ( this._config.productIdentifierOrBarcode === "Product identifier" ) { feedbackStrings = { - okNoVariants: __( "Your product is missing an identifier (like a GTIN code). " + - "You can add a product identifier via the \"Yoast SEO\" tab in the Product data box", "wordpress-seo" ), + okNoVariants: __( "Your product is missing an identifier (like a GTIN code)", "wordpress-seo" ), goodNoVariants: __( "Your product has an identifier", "wordpress-seo" ), okWithVariants: __( "Not all your product variants have an identifier", "wordpress-seo" ), goodWithVariants: __( "All your product variants have an identifier", "wordpress-seo" ), From 590d74de03a33557592c58f588becd234830e3c3 Mon Sep 17 00:00:00 2001 From: Hans-Christiaan Date: Fri, 28 Apr 2023 13:19:49 +0200 Subject: [PATCH 04/52] Extracted method from handleEditButtonClick, to reduce its complexity. --- .../src/components/contentAnalysis/Results.js | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/packages/js/src/components/contentAnalysis/Results.js b/packages/js/src/components/contentAnalysis/Results.js index 3ff4790b7f3..92d6b3ee52c 100644 --- a/packages/js/src/components/contentAnalysis/Results.js +++ b/packages/js/src/components/contentAnalysis/Results.js @@ -180,7 +180,7 @@ class Results extends Component { */ handleEditButtonClick( id ) { // Whether the user is in the metabox or sidebar. - let inputFieldLocation = this.props.location; + const inputFieldLocation = this.props.location; if ( id === "functionWordsInKeyphrase" || id === "keyphraseLength" ) { this.focusOnKeyphraseField( inputFieldLocation ); @@ -191,31 +191,42 @@ class Results extends Component { * For all the other assessments that have an edit button, we need to jump to the relevant Google preview fields. * (metadescription, slug, or title). If the user is in the sidebar, these are accessed through a modal. So if the * inputFieldLocation string is 'sidebar' it should now be changed to 'modal'. - */ + */ if ( [ "metaDescriptionKeyword", "metaDescriptionLength", "titleWidth", "keyphraseInSEOTitle", "slugKeyword" ].includes( id ) ) { - if ( inputFieldLocation === "sidebar" ) { - inputFieldLocation = "modal"; - // Open the modal. - document.getElementById( "yoast-google-preview-modal-open-button" ).click(); - // Wait for the input field elements to become available, then focus on the relevant field. - setTimeout( () => this.focusOnGooglePreviewField( id, inputFieldLocation ), 500 ); - } else { - const googlePreviewCollapsible = document.getElementById( "yoast-snippet-editor-metabox" ); - // Check if the collapsible is closed before clicking on it. - if ( googlePreviewCollapsible && googlePreviewCollapsible.getAttribute( "aria-expanded" ) === "false" ) { - // If it is closed, click on it to open it, and wait a bit before focusing on the edit field. - googlePreviewCollapsible.click(); - setTimeout( () => this.focusOnGooglePreviewField( id, inputFieldLocation ), 100 ); - } else { - // Collapsible already open, we can click on the field directly. - this.focusOnGooglePreviewField( id, inputFieldLocation ); - } - } + this.handleGooglePreviewFocus( inputFieldLocation, id ); } doAction( "yoast.focus.input", id ); } + /** + * Handle focus on Google Preview elements, when an edit button is clicked. + * + * @param {string} inputFieldLocation The location of the input field. + * @param {string} id The id of the input field. + * + * @returns {void} + */ + handleGooglePreviewFocus( inputFieldLocation, id ) { + if ( inputFieldLocation === "sidebar" ) { + // Open the modal. + document.getElementById( "yoast-google-preview-modal-open-button" ).click(); + // Wait for the input field elements to become available, then focus on the relevant field. + setTimeout( () => this.focusOnGooglePreviewField( id, "modal" ), 500 ); + } else { + const googlePreviewCollapsible = document.getElementById( "yoast-snippet-editor-metabox" ); + // Check if the collapsible is closed before clicking on it. + if ( googlePreviewCollapsible && googlePreviewCollapsible.getAttribute( "aria-expanded" ) === "false" ) { + // If it is closed, click on it to open it, and wait a bit before focusing on the edit field. + googlePreviewCollapsible.click(); + setTimeout( () => this.focusOnGooglePreviewField( id, inputFieldLocation ), 100 ); + } else { + // Collapsible already open, we can click on the field directly. + this.focusOnGooglePreviewField( id, inputFieldLocation ); + } + } + } + /** * Removes all markers. * From 1f4a3dc8849ac09ea1d0b73b82bedf4cf3b6e7e7 Mon Sep 17 00:00:00 2001 From: Hans-Christiaan Braun Date: Wed, 3 May 2023 09:53:52 +0200 Subject: [PATCH 05/52] Fixed typo in doc comment. Co-authored-by: Aida Marfuaty <48715883+FAMarfuaty@users.noreply.github.com> --- packages/js/src/components/contentAnalysis/Results.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/src/components/contentAnalysis/Results.js b/packages/js/src/components/contentAnalysis/Results.js index 92d6b3ee52c..bd30e5e3e70 100644 --- a/packages/js/src/components/contentAnalysis/Results.js +++ b/packages/js/src/components/contentAnalysis/Results.js @@ -200,7 +200,7 @@ class Results extends Component { } /** - * Handle focus on Google Preview elements, when an edit button is clicked. + * Handles focus on Google Preview elements, when an edit button is clicked. * * @param {string} inputFieldLocation The location of the input field. * @param {string} id The id of the input field. From 55b0978a7fb391296b4a30ff3e90eda6f3e5f34c Mon Sep 17 00:00:00 2001 From: Hans-Christiaan Date: Mon, 8 May 2023 09:27:32 +0200 Subject: [PATCH 06/52] Changed l10n domain from wordpress-seo to yoast-woo-seo for edit buttons in WooCommerce SEO assessments. --- .../src/scoring/assessments/seo/ProductIdentifiersAssessment.js | 2 +- .../src/scoring/assessments/seo/ProductSKUAssessment.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js index 170846cb12e..db9626f468c 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js @@ -55,7 +55,7 @@ export default class ProductIdentifiersAssessment extends Assessment { if ( assessmentResult.getScore() < 9 ) { assessmentResult.setHasJumps( true ); - assessmentResult.setEditFieldName( __( "Product identifiers", "wordpress-seo" ) ); + assessmentResult.setEditFieldName( __( "Product identifiers", "yoast-woo-seo" ) ); } return assessmentResult; diff --git a/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js index b7c25ea6fef..fb460c64cb8 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js @@ -54,7 +54,7 @@ export default class ProductSKUAssessment extends Assessment { if ( assessmentResult.getScore() < 9 ) { assessmentResult.setHasJumps( true ); - assessmentResult.setEditFieldName( __( "SKU", "wordpress-seo" ) ); + assessmentResult.setEditFieldName( __( "SKU", "yoast-woo-seo" ) ); } return assessmentResult; From 1b22c6bd80cd106b3a324f1b1999f6ed7442a896 Mon Sep 17 00:00:00 2001 From: Hans-Christiaan Date: Wed, 10 May 2023 14:41:17 +0200 Subject: [PATCH 07/52] Add an option to toggle the edit button for the Product identifier assessment. --- .../scoring/assessments/seo/ProductIdentifiersAssessment.js | 3 ++- .../src/scoring/assessments/seo/ProductSKUAssessment.js | 2 +- .../src/scoring/productPages/cornerstone/seoAssessor.js | 2 ++ packages/yoastseo/src/scoring/productPages/seoAssessor.js | 2 ++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js index db9626f468c..6f1c40cfc16 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/ProductIdentifiersAssessment.js @@ -27,6 +27,7 @@ export default class ProductIdentifiersAssessment extends Assessment { urlCallToAction: createAnchorOpeningTag( "https://yoa.st/4lz" ), assessVariants: true, productIdentifierOrBarcode: "Product identifier", + shouldShowEditButton: true, }; this.identifier = "productIdentifier"; @@ -53,7 +54,7 @@ export default class ProductIdentifiersAssessment extends Assessment { assessmentResult.setText( result.text ); } - if ( assessmentResult.getScore() < 9 ) { + if ( assessmentResult.getScore() < 9 && this._config.shouldShowEditButton ) { assessmentResult.setHasJumps( true ); assessmentResult.setEditFieldName( __( "Product identifiers", "yoast-woo-seo" ) ); } diff --git a/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js index fb460c64cb8..80b83f427e6 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/ProductSKUAssessment.js @@ -52,7 +52,7 @@ export default class ProductSKUAssessment extends Assessment { assessmentResult.setText( result.text ); } - if ( assessmentResult.getScore() < 9 ) { + if ( assessmentResult.getScore() < 9 && this._config.shouldShowEditButton ) { assessmentResult.setHasJumps( true ); assessmentResult.setEditFieldName( __( "SKU", "yoast-woo-seo" ) ); } diff --git a/packages/yoastseo/src/scoring/productPages/cornerstone/seoAssessor.js b/packages/yoastseo/src/scoring/productPages/cornerstone/seoAssessor.js index ec1b636187d..4627e57b751 100644 --- a/packages/yoastseo/src/scoring/productPages/cornerstone/seoAssessor.js +++ b/packages/yoastseo/src/scoring/productPages/cornerstone/seoAssessor.js @@ -146,12 +146,14 @@ const ProductCornerstoneSEOAssessor = function( researcher, options ) { urlCallToAction: createAnchorOpeningTag( options.productIdentifierCTAUrl ), assessVariants: options.assessVariants, productIdentifierOrBarcode: options.productIdentifierOrBarcode, + shouldShowEditButton: options.shouldShowEditButtons, } ), new ProductSKUAssessment( { urlTitle: createAnchorOpeningTag( options.productSKUUrlTitle ), urlCallToAction: createAnchorOpeningTag( options.productSKUCTAUrl ), assessVariants: options.assessVariants, addSKULocation: options.addSKULocation, + shouldShowEditButton: options.shouldShowEditButtons, } ), ]; }; diff --git a/packages/yoastseo/src/scoring/productPages/seoAssessor.js b/packages/yoastseo/src/scoring/productPages/seoAssessor.js index 4a69f4de3dc..09940298d33 100644 --- a/packages/yoastseo/src/scoring/productPages/seoAssessor.js +++ b/packages/yoastseo/src/scoring/productPages/seoAssessor.js @@ -127,12 +127,14 @@ const ProductSEOAssessor = function( researcher, options ) { urlCallToAction: createAnchorOpeningTag( options.productIdentifierCTAUrl ), assessVariants: options.assessVariants, productIdentifierOrBarcode: options.productIdentifierOrBarcode, + shouldShowEditButton: options.shouldShowEditButtons, } ), new ProductSKUAssessment( { urlTitle: createAnchorOpeningTag( options.productSKUUrlTitle ), urlCallToAction: createAnchorOpeningTag( options.productSKUCTAUrl ), assessVariants: options.assessVariants, addSKULocation: options.addSKULocation, + shouldShowEditButton: options.shouldShowEditButtons, } ), ]; }; From 951d2b1ada7a3abfb0338440048779fadeb87052 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 23 May 2023 17:00:07 +0200 Subject: [PATCH 08/52] Add a new variable for the filtered SEO title in the mapped data --- .../src/snippet-editor/SnippetEditor.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js index cd7aaec4fb2..80b0130612a 100644 --- a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js +++ b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js @@ -221,7 +221,8 @@ class SnippetEditor extends React.Component { const data = this.mapDataToMeasurements( nextProps.data, nextProps.replacementVariables ); this.setState( { - titleLengthProgress: getTitleProgress( data.title ), + // Here we use the filtered SEO title for the SEO title progress calculation. + titleLengthProgress: getTitleProgress( data.filteredSEOTitle ), descriptionLengthProgress: getDescriptionProgress( data.description, nextProps.date, @@ -466,10 +467,17 @@ class SnippetEditor extends React.Component { const shortenedBaseUrl = baseUrl.replace( /^https?:\/\//i, "" ); + // The regex of the variables we want to exclude from the title. + const excludedVars = new RegExp( "(%%sep%%|%%sitename%%)", "g" ); + // The filtered title is the SEO title without separator and site title. + // This data will be used in calculating the SEO title width. + const filteredTitle = originalData.title.replaceAll( excludedVars, "" ); + const mappedData = { title: this.processReplacementVariables( originalData.title, replacementVariables ), url: baseUrl + originalData.slug, description: description, + filteredSEOTitle: this.processReplacementVariables( filteredTitle, replacementVariables ), }; const context = { @@ -481,7 +489,6 @@ class SnippetEditor extends React.Component { return mapEditorDataToPreview( mappedData, context ); } - return mappedData; } From 838f97448e4c206251ce106e387cea96099a2f61 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 23 May 2023 17:01:18 +0200 Subject: [PATCH 09/52] Also replace variables in the filtered SEO title --- .../js/src/elementor/containers/SnippetEditor.js | 2 ++ .../js/src/helpers/replacementVariableHelpers.js | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/js/src/elementor/containers/SnippetEditor.js b/packages/js/src/elementor/containers/SnippetEditor.js index 0f41586e3c1..a7e6a938b68 100644 --- a/packages/js/src/elementor/containers/SnippetEditor.js +++ b/packages/js/src/elementor/containers/SnippetEditor.js @@ -20,6 +20,7 @@ const { stripHTMLTags } = strings; * @param {string} data.title The snippet preview title. * @param {string} data.url The snippet preview url: baseUrl with the slug. * @param {string} data.description The snippet preview description. + * @param {string} data.filteredSEOTitle The SEO title without separator and site title. * @param {Object} context The context surrounding the snippet editor form data. * @param {string} context.shortenedBaseUrl The baseUrl of the snippet preview url. * @@ -57,6 +58,7 @@ const mapEditorDataToPreview = ( data, context ) => { url: data.url, title: stripHTMLTags( applyModifications( "data_page_title", data.title ) ), description: stripHTMLTags( applyModifications( "data_meta_desc", data.description ) ), + filteredSEOTitle: stripHTMLTags( applyModifications( "data_page_title", data.filteredSEOTitle ) ), }; }; diff --git a/packages/js/src/helpers/replacementVariableHelpers.js b/packages/js/src/helpers/replacementVariableHelpers.js index 19052f1cf38..71d14d3ced5 100644 --- a/packages/js/src/helpers/replacementVariableHelpers.js +++ b/packages/js/src/helpers/replacementVariableHelpers.js @@ -241,10 +241,11 @@ export function excerptFromContent( content, limit = 156 ) { /** * Runs the legacy replaceVariables function on the data in the snippet preview. * - * @param {Object} data The snippet preview data object. - * @param {string} data.title The snippet preview title. - * @param {string} data.url The snippet preview url: baseUrl with the slug. - * @param {string} data.description The snippet preview description. + * @param {Object} data The snippet preview data object. + * @param {string} data.title The snippet preview title. + * @param {string} data.url The snippet preview url: baseUrl with the slug. + * @param {string} data.description The snippet preview description. + * @param {string} data.filteredSEOTitle The SEO title without separator and site title. * * @returns {Object} Returns the data object in which the placeholders have been replaced. */ @@ -255,6 +256,7 @@ const legacyReplaceUsingPlugin = function( data ) { url: data.url, title: stripHTMLTags( replaceVariables( data.title ) ), description: stripHTMLTags( replaceVariables( data.description ) ), + filteredSEOTitle: stripHTMLTags( replaceVariables( data.filteredSEOTitle ) ), }; }; @@ -265,6 +267,7 @@ const legacyReplaceUsingPlugin = function( data ) { * @param {string} data.title The snippet preview title. * @param {string} data.url The snippet preview url: baseUrl with the slug. * @param {string} data.description The snippet preview description. + * @param {string} data.filteredSEOTitle The SEO title without separator and site title. * * @returns {Object} Returns the data object in which the placeholders have been replaced. */ @@ -281,5 +284,6 @@ export const applyReplaceUsingPlugin = function( data ) { url: data.url, title: stripHTMLTags( applyModifications( "data_page_title", data.title ) ), description: stripHTMLTags( applyModifications( "data_meta_desc", data.description ) ), + filteredSEOTitle: stripHTMLTags( applyModifications( "data_page_title", data.filteredSEOTitle ) ), }; }; From 87519a12d1812a7960f27b0504993bf5b1a5a13a Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 23 May 2023 17:01:50 +0200 Subject: [PATCH 10/52] use the filtered SEO title for the Paper's titleWidth --- packages/js/src/analysis/collectAnalysisData.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/js/src/analysis/collectAnalysisData.js b/packages/js/src/analysis/collectAnalysisData.js index 3969af5c8e1..70fe78a3b33 100644 --- a/packages/js/src/analysis/collectAnalysisData.js +++ b/packages/js/src/analysis/collectAnalysisData.js @@ -98,7 +98,9 @@ export default function collectAnalysisData( editorData, store, customAnalysisDa data.wpBlocks = pluggable._applyModifications( "wpBlocks", data.wpBlocks ); } - data.titleWidth = measureTextWidth( data.title ); + const filteredSEOTitle = storeData.analysisData.snippet.filteredSEOTitle; + // When measuring the SEO title width, we exclude the separator and the site title from the calculation. + data.titleWidth = measureTextWidth( filteredSEOTitle || storeData.snippetEditor.data.title ); data.locale = getContentLocale(); data.writingDirection = getWritingDirection(); From d3ddc034fe3009363c725a6cd2e2d558df3ff79a Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 23 May 2023 17:09:12 +0200 Subject: [PATCH 11/52] use the filtered SEO title for title progress bar --- .../src/snippet-editor/SnippetEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js index 80b0130612a..e8eb33caf93 100644 --- a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js +++ b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js @@ -143,7 +143,7 @@ class SnippetEditor extends React.Component { isOpen: ! props.showCloseButton, activeField: null, hoveredField: null, - titleLengthProgress: getTitleProgress( measurementData.title ), + titleLengthProgress: getTitleProgress( measurementData.filteredSEOTitle ), descriptionLengthProgress: getDescriptionProgress( measurementData.description, this.props.date, From 3ba67c0fa499bb67415a930dee06a9e3ea6713c3 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Wed, 24 May 2023 10:27:02 +0200 Subject: [PATCH 12/52] Adjust unit tests --- packages/js/tests/containers/mapEditorDataToPreview.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/js/tests/containers/mapEditorDataToPreview.test.js b/packages/js/tests/containers/mapEditorDataToPreview.test.js index 3810b701720..86a8054ca48 100644 --- a/packages/js/tests/containers/mapEditorDataToPreview.test.js +++ b/packages/js/tests/containers/mapEditorDataToPreview.test.js @@ -8,6 +8,7 @@ describe( "mapEditorDataToPreview", () => { title: "", url: "local.wordpress.test/my URL is awesome", description: "", + filteredSEOTitle: "", }; context = { shortenedBaseUrl: "local.wordpress.test/", @@ -20,6 +21,7 @@ describe( "mapEditorDataToPreview", () => { description: "no more spaces", title: "", url: "local.wordpress.test/my-URL-is-awesome", + filteredSEOTitle: "", }; dataObject.description = exampleDescription; @@ -34,6 +36,7 @@ describe( "mapEditorDataToPreview", () => { description: "Yay for spaces", title: "", url: "local.wordpress.test/my-URL-is-awesome", + filteredSEOTitle: "", }; dataObject.description = exampleDescription; @@ -48,6 +51,7 @@ describe( "mapEditorDataToPreview", () => { description: "", title: "", url: "local.wordpress.test/only-one-dash", + filteredSEOTitle: "", }; dataObject.url = exampleUrl; @@ -62,6 +66,7 @@ describe( "mapEditorDataToPreview", () => { description: "", title: "", url: "local.wordpress.test/only-one-dash", + filteredSEOTitle: "", }; dataObject.url = exampleUrl; From 5a9d213c147a290da46670c9e3801679a6976235 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Wed, 24 May 2023 11:33:35 +0200 Subject: [PATCH 13/52] Use replace instead of replaceAll --- .../src/snippet-editor/SnippetEditor.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js index e8eb33caf93..a96edae4ad2 100644 --- a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js +++ b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js @@ -449,8 +449,9 @@ class SnippetEditor extends React.Component { * Maps the data from to be suitable for measurement. * * The data that is measured is not exactly the same as the data that - * is in the preview, because the metadescription placeholder shouldn't - * be measured. + * is in the preview, because the meta description placeholder shouldn't + * be measured. Additionally, the separator and site title should also be filtered out of the SEO title + * before the width is measured. * * @param {Object} originalData The data from the form. * @param {array} replacementVariables The replacement variables to use. Taken from the props by default. @@ -471,7 +472,7 @@ class SnippetEditor extends React.Component { const excludedVars = new RegExp( "(%%sep%%|%%sitename%%)", "g" ); // The filtered title is the SEO title without separator and site title. // This data will be used in calculating the SEO title width. - const filteredTitle = originalData.title.replaceAll( excludedVars, "" ); + const filteredTitle = originalData.title.replace( excludedVars, "" ); const mappedData = { title: this.processReplacementVariables( originalData.title, replacementVariables ), From 40986b7447f87f52ad9f1141766c91b0382c4d57 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Wed, 24 May 2023 13:47:52 +0200 Subject: [PATCH 14/52] Add unit tests --- .../tests/SnippetEditorTest.js | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/search-metadata-previews/tests/SnippetEditorTest.js b/packages/search-metadata-previews/tests/SnippetEditorTest.js index 8cc28afa867..9be72144a8e 100644 --- a/packages/search-metadata-previews/tests/SnippetEditorTest.js +++ b/packages/search-metadata-previews/tests/SnippetEditorTest.js @@ -14,7 +14,6 @@ const defaultArgs = { baseUrl: "http://example.org/", data: defaultData, onChange: () => {}, - }; /** @@ -253,4 +252,27 @@ describe( "SnippetEditor", () => { expect( isDirty ).toBe( true ); } ); } ); + describe( "mapDataToMeasurements", () => { + let editor, data; + beforeEach( () => { + editor = mountWithArgs( {} ); + data = { + title: "Tortoiseshell cats %%sep%% %%sitename%%", + description: " The cool tortie everyone loves! ", + slug: "tortie-cats", + }; + } ); + it( "returns the filtered SEO title without separator and site title", () => { + expect( editor.instance().mapDataToMeasurements( data ).filteredSEOTitle ).toBe( "Tortoiseshell cats " ); + editor.unmount(); + } ); + it( "returns the correct url: baseURL + slug", () => { + expect( editor.instance().mapDataToMeasurements( data ).url ).toBe( "http://example.org/tortie-cats" ); + editor.unmount(); + } ); + it( "returns the description with multiple spaces stripped", () => { + expect( editor.instance().mapDataToMeasurements( data ).description ).toBe( "The cool tortie everyone loves!" ); + editor.unmount(); + } ); + } ); } ); From 451198afa5bd62222b3a625115f2e62f7ed4dbb9 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Wed, 24 May 2023 13:53:57 +0200 Subject: [PATCH 15/52] Move the declaration of the regex of the exlcuded variables --- .../src/snippet-editor/SnippetEditor.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js index a96edae4ad2..658538fc9f6 100644 --- a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js +++ b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js @@ -46,6 +46,9 @@ const CloseEditorButton = styled( SnippetEditorButton )` margin-top: 24px; `; +// The regex of the variables we want to exclude from the SEO title. +const excludedVars = new RegExp( "(%%sep%%|%%sitename%%)", "g" ); + /** * Gets the title progress. * @@ -84,7 +87,7 @@ function getTitleProgress( title ) { function getDescriptionProgress( description, date, isCornerstone, isTaxonomy, locale ) { const descriptionLength = languageProcessing.countMetaDescriptionLength( date, description ); - // Override the default config if the cornerstone content toggle is on and it is not a taxonomy page. + // Override the default config if the cornerstone content toggle is on, and it is not a taxonomy page. const metaDescriptionLengthAssessment = ( isCornerstone && ! isTaxonomy ) ? new MetaDescriptionLengthAssessment( { scores: { tooLong: 3, @@ -234,7 +237,7 @@ class SnippetEditor extends React.Component { if ( this.haveReplaceVarsChanged( this.props.replacementVariables, nextProps.replacementVariables ) ) { /* - * Make sure that changes to the replace vars (e.g. title, category, tags) get reflected on the + * Make sure that changes to the replacement vars (e.g. title, category, tags) get reflected on the * analysis data on the store (used in, among other things, the SEO analysis). */ this.props.onChangeAnalysisData( data ); @@ -468,8 +471,6 @@ class SnippetEditor extends React.Component { const shortenedBaseUrl = baseUrl.replace( /^https?:\/\//i, "" ); - // The regex of the variables we want to exclude from the title. - const excludedVars = new RegExp( "(%%sep%%|%%sitename%%)", "g" ); // The filtered title is the SEO title without separator and site title. // This data will be used in calculating the SEO title width. const filteredTitle = originalData.title.replace( excludedVars, "" ); @@ -497,7 +498,7 @@ class SnippetEditor extends React.Component { * Maps the passed data to be suitable for the preview. * * The data that is in the preview is not exactly the same as the data - * that is measured (see above), because the metadescription placeholder + * that is measured (see above), because the meta description placeholder * shouldn't be measured. * * @param {Object} originalData The data from the form. @@ -541,7 +542,7 @@ class SnippetEditor extends React.Component { } /** - * Sets a reference to the edit button so we can move focus to it. + * Sets a reference to the edit button, so we can move focus to it. * * @param {Object} ref The edit button element. * From 8761a228b3b02e6b40d794248fcec81161559b1a Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Fri, 26 May 2023 14:06:23 +0200 Subject: [PATCH 16/52] Adjust comment --- .../src/snippet-editor/SnippetEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js index 658538fc9f6..945ecc2c963 100644 --- a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js +++ b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js @@ -46,7 +46,7 @@ const CloseEditorButton = styled( SnippetEditorButton )` margin-top: 24px; `; -// The regex of the variables we want to exclude from the SEO title. +// The regex for the replacement variables we want to exclude from the SEO title before we measure the width. const excludedVars = new RegExp( "(%%sep%%|%%sitename%%)", "g" ); /** From 953d2db27af46fa8e74cb4dd90a70b96230df825 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Fri, 26 May 2023 14:19:02 +0200 Subject: [PATCH 17/52] Adjust comment --- .../src/snippet-editor/SnippetEditor.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js index 945ecc2c963..e34f5d99800 100644 --- a/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js +++ b/packages/search-metadata-previews/src/snippet-editor/SnippetEditor.js @@ -50,11 +50,11 @@ const CloseEditorButton = styled( SnippetEditorButton )` const excludedVars = new RegExp( "(%%sep%%|%%sitename%%)", "g" ); /** - * Gets the title progress. + * Gets the SEO title progress. * - * @param {string} title The title. + * @param {string} title The SEO title. * - * @returns {Object} The title progress. + * @returns {Object} The SEO title progress. */ function getTitleProgress( title ) { const titleWidth = measureTextWidth( title ); @@ -117,7 +117,7 @@ class SnippetEditor extends React.Component { * @param {Object[]} props.recommendedReplacementVariables The recommended replacement variables for this editor. * @param {Object} props.data The initial editor data. * @param {string} props.keyword The focus keyword. - * @param {string} props.data.title The initial title. + * @param {string} props.data.title The initial SEO title. * @param {string} props.data.slug The initial slug. * @param {string} props.data.description The initial description. * @param {bool} props.isCornerstone Whether the cornerstone content toggle is on or off. @@ -125,7 +125,7 @@ class SnippetEditor extends React.Component { * @param {string} props.baseUrl The base URL to use for the preview. * @param {string} props.mode The mode the editor should be in. * @param {Function} props.onChange Called when the data changes. - * @param {Object} props.titleLengthProgress The values for the title length assessment. + * @param {Object} props.titleLengthProgress The values for the SEO title length assessment. * @param {Object} props.descriptionLengthProgress The values for the description length assessment. * @param {Function} props.mapEditorDataToPreview Function to map the editor data to data for the preview. * @param {Function} props.applyReplacementVariables Function that overrides default replacement variables application with a custom one. From 06e719b3b210dc0beb0ce1bfab3c6ccbca480b86 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Fri, 26 May 2023 14:41:55 +0200 Subject: [PATCH 18/52] Adjust JSDoc --- .../assessments/seo/PageTitleWidthAssessment.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/yoastseo/src/scoring/assessments/seo/PageTitleWidthAssessment.js b/packages/yoastseo/src/scoring/assessments/seo/PageTitleWidthAssessment.js index e026d6d49b2..d9118bb5a76 100644 --- a/packages/yoastseo/src/scoring/assessments/seo/PageTitleWidthAssessment.js +++ b/packages/yoastseo/src/scoring/assessments/seo/PageTitleWidthAssessment.js @@ -7,8 +7,9 @@ import { createAnchorOpeningTag } from "../../../helpers/shortlinker"; import AssessmentResult from "../../../values/AssessmentResult"; const maximumLength = 600; + /** - * Represents the assessment that will calculate if the width of the page title is correct. + * Represents the assessment that assesses the SEO title width and gives the feedback accordingly. */ export default class PageTitleWidthAssessment extends Assessment { /** @@ -75,9 +76,9 @@ export default class PageTitleWidthAssessment extends Assessment { } /** - * Returns the score for the pageTitleWidth + * Returns the score for the SEO title width calculation. * - * @param {number} pageTitleWidth The width of the pageTitle. + * @param {number} pageTitleWidth The width of the SEO title. * * @returns {number} The calculated score. */ @@ -98,9 +99,9 @@ export default class PageTitleWidthAssessment extends Assessment { } /** - * Translates the pageTitleWidth score to a message the user can understand. + * Translates the score of the SEO title width calculation to a message the user can understand. * - * @param {number} pageTitleWidth The width of the pageTitle. + * @param {number} pageTitleWidth The width of the SEO title. * * @returns {string} The translated string. */ From 6c6c3d246b12ae6c720c7e9939d56ae5fb12b463 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Fri, 26 May 2023 16:32:48 +0200 Subject: [PATCH 19/52] Adapt the analysis documentation --- packages/yoastseo/src/scoring/assessments/SCORING SEO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/yoastseo/src/scoring/assessments/SCORING SEO.md b/packages/yoastseo/src/scoring/assessments/SCORING SEO.md index 8c749513c45..e4321037659 100644 --- a/packages/yoastseo/src/scoring/assessments/SCORING SEO.md +++ b/packages/yoastseo/src/scoring/assessments/SCORING SEO.md @@ -289,7 +289,7 @@ With the example keyphrase `cat and dog` the following criteria would apply to c | Green | 9 | All internal links are followed | **Internal links**: You have enough internal links. Good job! | ### 4) SEO Title width -**What it does**: Checks if the SEO title has a good length. Note that this assessment checks the SEO title as it appears in the snippet preview. Therefore, it also takes into account the content from replacement variables. +**What it does**: Checks if the SEO title has a good length. Note that this assessment checks the SEO title as it appears in the snippet preview. Therefore, it also takes into account the content from replacement variables. However, we exclude the separator and the site title replacement variables from the calculation. **When it applies**: Always. From bd4e0fca30bd151cf5b869f39c410e9f20804186 Mon Sep 17 00:00:00 2001 From: Mykola Shlyakhtun Date: Mon, 26 Jun 2023 09:18:57 +0200 Subject: [PATCH 20/52] Add default allowlist to "Remove unregistered URL parameters"#20066 --- src/helpers/crawl-cleanup-helper.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/helpers/crawl-cleanup-helper.php b/src/helpers/crawl-cleanup-helper.php index 935d7676457..a26b20ae877 100644 --- a/src/helpers/crawl-cleanup-helper.php +++ b/src/helpers/crawl-cleanup-helper.php @@ -76,6 +76,16 @@ public function should_avoid_redirect() { * @return array The list of the allowed extra vars. */ public function get_allowed_extravars() { + $default_allowed_extravars = [ + 'utm_source', + 'utm_medium', + 'utm_campaign', + 'utm_term', + 'utm_content', + 'gclid', + 'gtm_debug' + ]; + /** * Filter: 'Yoast\WP\SEO\allowlist_permalink_vars' - Allows plugins to register their own variables not to clean. * @@ -83,7 +93,8 @@ public function get_allowed_extravars() { * * @param array $allowed_extravars The list of the allowed vars (empty by default). */ - $allowed_extravars = \apply_filters( 'Yoast\WP\SEO\allowlist_permalink_vars', [] ); + + $allowed_extravars = \apply_filters('Yoast\WP\SEO\allowlist_permalink_vars', $default_allowed_extravars); $clean_permalinks_extra_variables = $this->options_helper->get( 'clean_permalinks_extra_variables' ); From cb4d946fa7b029a910c05600aa5c380c7bfbdf8d Mon Sep 17 00:00:00 2001 From: Mykola Shlyakhtun Date: Tue, 27 Jun 2023 03:18:31 +0200 Subject: [PATCH 21/52] Add default allowlist to "Remove unregistered URL parameters"#20066 Fix linting --- src/helpers/crawl-cleanup-helper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/crawl-cleanup-helper.php b/src/helpers/crawl-cleanup-helper.php index a26b20ae877..172fcfd7566 100644 --- a/src/helpers/crawl-cleanup-helper.php +++ b/src/helpers/crawl-cleanup-helper.php @@ -83,7 +83,7 @@ public function get_allowed_extravars() { 'utm_term', 'utm_content', 'gclid', - 'gtm_debug' + 'gtm_debug', ]; /** @@ -94,7 +94,7 @@ public function get_allowed_extravars() { * @param array $allowed_extravars The list of the allowed vars (empty by default). */ - $allowed_extravars = \apply_filters('Yoast\WP\SEO\allowlist_permalink_vars', $default_allowed_extravars); + $allowed_extravars = \apply_filters( 'Yoast\WP\SEO\allowlist_permalink_vars', $default_allowed_extravars ); $clean_permalinks_extra_variables = $this->options_helper->get( 'clean_permalinks_extra_variables' ); From 9316427a1a1e3db16f47749153d73e8c161c8f87 Mon Sep 17 00:00:00 2001 From: Mykola Shlyakhtun Date: Tue, 27 Jun 2023 08:56:42 +0200 Subject: [PATCH 22/52] Add default allowlist to "Remove unregistered URL parameters"#20066 Fix tests --- tests/unit/helpers/crawl-cleanup-helper-test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/helpers/crawl-cleanup-helper-test.php b/tests/unit/helpers/crawl-cleanup-helper-test.php index d1578c2c121..ed5fabdad0f 100644 --- a/tests/unit/helpers/crawl-cleanup-helper-test.php +++ b/tests/unit/helpers/crawl-cleanup-helper-test.php @@ -147,7 +147,7 @@ public function test_get_allowed_extravars( $clean_permalinks_extra_variables, $ Monkey\Functions\expect( 'apply_filters' ) ->once() - ->with( 'Yoast\WP\SEO\allowlist_permalink_vars', [] ) + ->with( 'Yoast\WP\SEO\allowlist_permalink_vars', ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'gtm_debug'] ) ->andReturn( $permalink_vars ); $this->options_helper From 37002fee6ff765b4aad90f07fbb2bcbcef191e92 Mon Sep 17 00:00:00 2001 From: Mykola Shlyakhtun Date: Tue, 27 Jun 2023 09:07:01 +0200 Subject: [PATCH 23/52] Add default allowlist to "Remove unregistered URL parameters"#20066 Fix lint --- tests/unit/helpers/crawl-cleanup-helper-test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/helpers/crawl-cleanup-helper-test.php b/tests/unit/helpers/crawl-cleanup-helper-test.php index ed5fabdad0f..fcb93f65e64 100644 --- a/tests/unit/helpers/crawl-cleanup-helper-test.php +++ b/tests/unit/helpers/crawl-cleanup-helper-test.php @@ -147,7 +147,7 @@ public function test_get_allowed_extravars( $clean_permalinks_extra_variables, $ Monkey\Functions\expect( 'apply_filters' ) ->once() - ->with( 'Yoast\WP\SEO\allowlist_permalink_vars', ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'gtm_debug'] ) + ->with( 'Yoast\WP\SEO\allowlist_permalink_vars', [ 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'gtm_debug' ] ) ->andReturn( $permalink_vars ); $this->options_helper From cb77a05d784da6edd527a6de9471d2cecb599c47 Mon Sep 17 00:00:00 2001 From: hdvos Date: Thu, 29 Jun 2023 14:33:55 +0200 Subject: [PATCH 24/52] Add information about the allowlist for users. --- .../src/settings/routes/crawl-optimization.js | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/js/src/settings/routes/crawl-optimization.js b/packages/js/src/settings/routes/crawl-optimization.js index 032a555d6c6..84fe9aab6b8 100644 --- a/packages/js/src/settings/routes/crawl-optimization.js +++ b/packages/js/src/settings/routes/crawl-optimization.js @@ -226,15 +226,31 @@ const CrawlOptimization = () => { * %1$s expands to `301`. * %2$s and %3$s both expand to an example within a tag. */ - __( "Removes unknown URL parameters via a %1$s redirect. E.g., %2$s will be redirected to %3$s", "wordpress-seo" ), + __( "Removes unknown URL parameters via a %1$s redirect. " + + "E.g., %2$s will be redirected to %3$s " + + "Note that the following commonly-used parameters will not be removed: %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, and %10$s.", "wordpress-seo" ), "", "", - "" + "", + "", + "", + "", + "", + "", + "", + "" ), { code1: 301, code2: https://www.example.com/?unknown_parameter=yes, code3: https://www.example.com, + code4: utm_source, + code5: utm_medium, + code6: utm_campaign, + code7: utm_term, + code8: utm_content, + code9: gclid, + code10: gtm_debug, } ), cleanPermalinksExtraVariables: createInterpolateElement( From 7efcf0fe5f7a3400ebda8228b52822bc6b3a48df Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Fri, 30 Jun 2023 14:30:05 +0200 Subject: [PATCH 25/52] present the duration text and time in site language --- .../blocks/structured-data-blocks.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index 0be2cf7b214..bd0af749972 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -186,6 +186,23 @@ public function optimize_how_to_images( $attributes, $content ) { return $content; } + $regex = '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/'; + + $duration = \preg_match( + $regex, + $content, + $matches + ); + + $duration = $matches[ 3 ]; + + $content = \preg_replace( + $regex, + '

' . \__( $attributes[ 'defaultDurationText' ], "wordpress-seo" ) . ' ' . \__( $duration, "wordpress-seo" ) . '

', + $content, + 1 + ); + return $this->optimize_images( $attributes['steps'], 'text', $content ); } From 2e6d4ba33bc23e03f473ecddc39b793dd6f489ba Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Fri, 30 Jun 2023 14:47:51 +0200 Subject: [PATCH 26/52] present the duration text only when there is a match --- .../blocks/structured-data-blocks.php | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index bd0af749972..d0eca83414d 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -188,20 +188,18 @@ public function optimize_how_to_images( $attributes, $content ) { $regex = '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/'; - $duration = \preg_match( - $regex, - $content, - $matches - ); - - $duration = $matches[ 3 ]; - - $content = \preg_replace( - $regex, - '

' . \__( $attributes[ 'defaultDurationText' ], "wordpress-seo" ) . ' ' . \__( $duration, "wordpress-seo" ) . '

', - $content, - 1 - ); + $duration = \preg_match( $regex, $content, $matches ); + // Only retrieve the duration when there is a match. + if ( $matches ) { + $duration = $matches[ 3 ]; + var_dump( $attributes[ 'defaultDurationText' ], "TEXT" ); + $content = \preg_replace( + $regex, + '

' . \__( $attributes[ 'defaultDurationText' ], "wordpress-seo" ) . ' ' . \__( $duration, "wordpress-seo" ) . '

', + $content, + 1 + ); + } return $this->optimize_images( $attributes['steps'], 'text', $content ); } From 3858c2885cca2d69d01e5ce24ebdb90aa8b2b296 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Fri, 30 Jun 2023 15:26:05 +0200 Subject: [PATCH 27/52] Adjust code --- .../blocks/structured-data-blocks.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index d0eca83414d..7344d3e129e 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -191,11 +191,20 @@ public function optimize_how_to_images( $attributes, $content ) { $duration = \preg_match( $regex, $content, $matches ); // Only retrieve the duration when there is a match. if ( $matches ) { - $duration = $matches[ 3 ]; - var_dump( $attributes[ 'defaultDurationText' ], "TEXT" ); + $duration = $matches[3]; + $duration = \sprintf( + /* translators: */ + \__( '%s', 'wordpress-seo' ), + $duration + ); + $default_duration_text = \sprintf( + /* translators: %s: expands to 'Time needed:' */ + \__( '%s', 'wordpress-seo' ), + $attributes['defaultDurationText'] + ); $content = \preg_replace( $regex, - '

' . \__( $attributes[ 'defaultDurationText' ], "wordpress-seo" ) . ' ' . \__( $duration, "wordpress-seo" ) . '

', + '

' . $default_duration_text . ' ' . $duration . '

', $content, 1 ); From c4db9ddd15194876c7d46920303e781d2fe64e7b Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Fri, 30 Jun 2023 16:43:09 +0200 Subject: [PATCH 28/52] Adjust code --- .../blocks/structured-data-blocks.php | 73 +++++++++++++------ 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index 7344d3e129e..54f14f7d6b1 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -173,6 +173,44 @@ public function optimize_faq_images( $attributes, $content ) { return $this->optimize_images( $attributes['questions'], 'answer', $content ); } + private function transform_duration_to_string( $days, $hours, $minutes ) { + $strings = []; + if ( $days !== 0 ) { + $strings[] = sprintf( \_n( '%d day', '%d days', $days, 'wordpress-seo' ), $days ); + } + if ( $hours !== 0 ) { + $strings[] = \sprintf( \_n( '%d hour', '%d hours', $hours, 'wordpress-seo' ), $hours ); + } + if ( $minutes !== 0 ) { + $strings[] = \sprintf( \_n( '%d minute', '%d minutes', $minutes, 'wordpress-seo' ), $minutes ); + } + return $strings; + } + + private function build_duration_string( $attributes ) { + $elements = $this->transform_duration_to_string( $attributes['days'], $attributes['hours'], $attributes['minutes'] ); + $elements_length = count( $elements ); + + if ( $elements_length === 1 ) { + return $elements[0]; + } + if ( $elements_length === 2 ) { + return \sprintf( + /* translators: %s expands to a unit of time (e.g. 1 day). */ + __( '%1$s and %2$s', 'wordpress-seo' ), + ...$elements + ); + } + if ( $elements_length === 3 ) { + return \sprintf( + /* translators: %s expands to a unit of time (e.g. 1 day). */ + \__( '%1$s, %2$s and %3$s', 'wordpress-seo' ), + ...$elements + ); + } + return ''; + } + /** * Optimizes images in the How-To blocks. * @@ -186,29 +224,20 @@ public function optimize_how_to_images( $attributes, $content ) { return $content; } - $regex = '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/'; - - $duration = \preg_match( $regex, $content, $matches ); - // Only retrieve the duration when there is a match. - if ( $matches ) { - $duration = $matches[3]; - $duration = \sprintf( - /* translators: */ - \__( '%s', 'wordpress-seo' ), - $duration - ); - $default_duration_text = \sprintf( + $regex = '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/'; + $duration = $this->build_duration_string( $attributes ); + var_dump( $duration, 'DURATION' ); + $duration_text = \sprintf( /* translators: %s: expands to 'Time needed:' */ - \__( '%s', 'wordpress-seo' ), - $attributes['defaultDurationText'] - ); - $content = \preg_replace( - $regex, - '

' . $default_duration_text . ' ' . $duration . '

', - $content, - 1 - ); - } + \__( '%s ', 'wordpress-seo' ), + $attributes['defaultDurationText'] + ); + $content = \preg_replace( + $regex, + '

' . $duration_text . $duration . '

', + $content, + 1 + ); return $this->optimize_images( $attributes['steps'], 'text', $content ); } From ff7fa70fff2a3df3788cbd1770a3a909e0e020cd Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Fri, 30 Jun 2023 17:00:22 +0200 Subject: [PATCH 29/52] adjust duration text --- .../blocks/structured-data-blocks.php | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index 54f14f7d6b1..d843d518a58 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -224,17 +224,12 @@ public function optimize_how_to_images( $attributes, $content ) { return $content; } - $regex = '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/'; - $duration = $this->build_duration_string( $attributes ); - var_dump( $duration, 'DURATION' ); - $duration_text = \sprintf( - /* translators: %s: expands to 'Time needed:' */ - \__( '%s ', 'wordpress-seo' ), - $attributes['defaultDurationText'] - ); - $content = \preg_replace( + $regex = '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/'; + $duration = $this->build_duration_string( $attributes ); + $duration_text = \__( 'Time needed:', 'wordpress-seo' ); + $content = \preg_replace( $regex, - '

' . $duration_text . $duration . '

', + '

' . $duration_text . ' ' . $duration . '

', $content, 1 ); From 4730c254a199e036f97b216067fcdd766a3d02de Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Mon, 3 Jul 2023 11:29:41 +0200 Subject: [PATCH 30/52] Add additional check for outputing the duration text --- .../blocks/structured-data-blocks.php | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index d843d518a58..6782f35a7a6 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -173,20 +173,47 @@ public function optimize_faq_images( $attributes, $content ) { return $this->optimize_images( $attributes['questions'], 'answer', $content ); } + /** + * Transforms the durations into a translated string containing the count, and either singular or plural unit. + * For example (in en-US): If 'days' is 1, it returns "1 day". If 'days' is 2, it returns "2 days". + * + * @param number $days Number of days. + * @param number $hours Number of hours. + * @param number $minutes Number of minutes. + * @return array Array of pluralized durations. + */ private function transform_duration_to_string( $days, $hours, $minutes ) { $strings = []; if ( $days !== 0 ) { - $strings[] = sprintf( \_n( '%d day', '%d days', $days, 'wordpress-seo' ), $days ); + $strings[] = \sprintf( + /* translators: %d expands to the number of day/days. */ + \_n( '%d day', '%d days', $days, 'wordpress-seo' ), + $days + ); } if ( $hours !== 0 ) { - $strings[] = \sprintf( \_n( '%d hour', '%d hours', $hours, 'wordpress-seo' ), $hours ); + $strings[] = \sprintf( + /* translators: %d expands to the number of hour/hours. */ + \_n( '%d hour', '%d hours', $hours, 'wordpress-seo' ), + $hours + ); } if ( $minutes !== 0 ) { - $strings[] = \sprintf( \_n( '%d minute', '%d minutes', $minutes, 'wordpress-seo' ), $minutes ); + $strings[] = \sprintf( + /* translators: %d expands to the number of minute/minutes. */ + \_n( '%d minute', '%d minutes', $minutes, 'wordpress-seo' ), + $minutes + ); } return $strings; } + /** + * Formats the durations into a translated string. + * + * @param array $attributes The attributes. + * @return string The formatted duration. + */ private function build_duration_string( $attributes ) { $elements = $this->transform_duration_to_string( $attributes['days'], $attributes['hours'], $attributes['minutes'] ); $elements_length = count( $elements ); @@ -197,7 +224,7 @@ private function build_duration_string( $attributes ) { if ( $elements_length === 2 ) { return \sprintf( /* translators: %s expands to a unit of time (e.g. 1 day). */ - __( '%1$s and %2$s', 'wordpress-seo' ), + \__( '%1$s and %2$s', 'wordpress-seo' ), ...$elements ); } @@ -224,11 +251,11 @@ public function optimize_how_to_images( $attributes, $content ) { return $content; } - $regex = '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/'; - $duration = $this->build_duration_string( $attributes ); - $duration_text = \__( 'Time needed:', 'wordpress-seo' ); + $duration = $this->build_duration_string( $attributes ); + // 'Time needed:' is the default duration text that will be shown if a user doesn't add one. + $duration_text = ( ! $attributes['durationText'] ) ? ( \__( 'Time needed:', 'wordpress-seo' ) ) : ( $attributes['durationText'] ); $content = \preg_replace( - $regex, + '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/', '

' . $duration_text . ' ' . $duration . '

', $content, 1 From 2161483c68a2f878890d3f6ca6f7c939bae3f9af Mon Sep 17 00:00:00 2001 From: hdvos Date: Mon, 3 Jul 2023 13:17:35 +0200 Subject: [PATCH 31/52] Split translation string in two and put the parameters in alphabetical order. --- .../src/settings/routes/crawl-optimization.js | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/js/src/settings/routes/crawl-optimization.js b/packages/js/src/settings/routes/crawl-optimization.js index 84fe9aab6b8..ace9b184f3e 100644 --- a/packages/js/src/settings/routes/crawl-optimization.js +++ b/packages/js/src/settings/routes/crawl-optimization.js @@ -227,11 +227,18 @@ const CrawlOptimization = () => { * %2$s and %3$s both expand to an example within a tag. */ __( "Removes unknown URL parameters via a %1$s redirect. " + - "E.g., %2$s will be redirected to %3$s " + - "Note that the following commonly-used parameters will not be removed: %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, and %10$s.", "wordpress-seo" ), + "E.g., %2$s will be redirected to %3$s ", "wordpress-seo" ), "", "", - "", + "" + ) + + /** + * translators: + * %1$s through %7$s all expand to a parameter name within a tag. + */ + sprintf( + __( "Note that the following commonly-used parameters will not be removed: %1$s, %2$s, %3$s, %4$s, %5$s, %6$s, and %7$s.", + "wordpress-seo" ), "", "", "", @@ -244,13 +251,13 @@ const CrawlOptimization = () => { code1: 301, code2: https://www.example.com/?unknown_parameter=yes, code3: https://www.example.com, - code4: utm_source, - code5: utm_medium, + code4: gclid, + code5: gtm_debug, code6: utm_campaign, - code7: utm_term, - code8: utm_content, - code9: gclid, - code10: gtm_debug, + code7: utm_content, + code8: utm_medium, + code9: utm_source, + code10: utm_term, } ), cleanPermalinksExtraVariables: createInterpolateElement( From 4fb5d789f8fdf7a386fe2221105f7e611d0c17cf Mon Sep 17 00:00:00 2001 From: hdvos Date: Mon, 3 Jul 2023 13:30:08 +0200 Subject: [PATCH 32/52] replace all with each. --- packages/js/src/settings/routes/crawl-optimization.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/src/settings/routes/crawl-optimization.js b/packages/js/src/settings/routes/crawl-optimization.js index ace9b184f3e..b36d4de8d3b 100644 --- a/packages/js/src/settings/routes/crawl-optimization.js +++ b/packages/js/src/settings/routes/crawl-optimization.js @@ -234,7 +234,7 @@ const CrawlOptimization = () => { ) + /** * translators: - * %1$s through %7$s all expand to a parameter name within a tag. + * %1$s through %7$s each expand to a parameter name within a tag. */ sprintf( __( "Note that the following commonly-used parameters will not be removed: %1$s, %2$s, %3$s, %4$s, %5$s, %6$s, and %7$s.", From 74583318ad2ab9c0015fb5e4d793fa0e5de7a564 Mon Sep 17 00:00:00 2001 From: hdvos Date: Mon, 3 Jul 2023 13:44:08 +0200 Subject: [PATCH 33/52] Separate trailing space from first translation string. --- packages/js/src/settings/routes/crawl-optimization.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/js/src/settings/routes/crawl-optimization.js b/packages/js/src/settings/routes/crawl-optimization.js index b36d4de8d3b..d0f04b6c9c8 100644 --- a/packages/js/src/settings/routes/crawl-optimization.js +++ b/packages/js/src/settings/routes/crawl-optimization.js @@ -227,11 +227,11 @@ const CrawlOptimization = () => { * %2$s and %3$s both expand to an example within a tag. */ __( "Removes unknown URL parameters via a %1$s redirect. " + - "E.g., %2$s will be redirected to %3$s ", "wordpress-seo" ), + "E.g., %2$s will be redirected to %3$s", "wordpress-seo" ), "", "", "" - ) + + ) + " " + /** * translators: * %1$s through %7$s each expand to a parameter name within a tag. From b11fa7020ab06c768023c5ef9b91d7e5de757324 Mon Sep 17 00:00:00 2001 From: hdvos Date: Mon, 3 Jul 2023 14:00:51 +0200 Subject: [PATCH 34/52] put translators comment inside sprintf --- packages/js/src/settings/routes/crawl-optimization.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/js/src/settings/routes/crawl-optimization.js b/packages/js/src/settings/routes/crawl-optimization.js index d0f04b6c9c8..631b603dea8 100644 --- a/packages/js/src/settings/routes/crawl-optimization.js +++ b/packages/js/src/settings/routes/crawl-optimization.js @@ -232,11 +232,11 @@ const CrawlOptimization = () => { "", "" ) + " " + - /** - * translators: - * %1$s through %7$s each expand to a parameter name within a tag. - */ sprintf( + /** + * translators: + * %1$s through %7$s each expand to a parameter name within a tag. For example gclid. + */ __( "Note that the following commonly-used parameters will not be removed: %1$s, %2$s, %3$s, %4$s, %5$s, %6$s, and %7$s.", "wordpress-seo" ), "", From 8e557f3ec1790dda0abd45ae24a701b7969147cd Mon Sep 17 00:00:00 2001 From: hdvos Date: Mon, 3 Jul 2023 14:08:34 +0200 Subject: [PATCH 35/52] remove unnecessary newlines. --- packages/js/src/settings/routes/crawl-optimization.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/js/src/settings/routes/crawl-optimization.js b/packages/js/src/settings/routes/crawl-optimization.js index 631b603dea8..7517704964e 100644 --- a/packages/js/src/settings/routes/crawl-optimization.js +++ b/packages/js/src/settings/routes/crawl-optimization.js @@ -226,8 +226,7 @@ const CrawlOptimization = () => { * %1$s expands to `301`. * %2$s and %3$s both expand to an example within a tag. */ - __( "Removes unknown URL parameters via a %1$s redirect. " + - "E.g., %2$s will be redirected to %3$s", "wordpress-seo" ), + __( "Removes unknown URL parameters via a %1$s redirect. E.g., %2$s will be redirected to %3$s", "wordpress-seo" ), "", "", "" From 0faab98da19bc55c1016a7acb1c22ac1840ce82a Mon Sep 17 00:00:00 2001 From: hdvos Date: Mon, 3 Jul 2023 14:11:26 +0200 Subject: [PATCH 36/52] remove unnecessary space. --- packages/js/src/settings/routes/crawl-optimization.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/src/settings/routes/crawl-optimization.js b/packages/js/src/settings/routes/crawl-optimization.js index 7517704964e..eb5cb318f37 100644 --- a/packages/js/src/settings/routes/crawl-optimization.js +++ b/packages/js/src/settings/routes/crawl-optimization.js @@ -230,7 +230,7 @@ const CrawlOptimization = () => { "", "", "" - ) + " " + + ) + sprintf( /** * translators: From f879dd171b5c7233117a0f348ad67d359fe963ff Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Mon, 3 Jul 2023 14:21:56 +0200 Subject: [PATCH 37/52] Adjust the check for outputting the duration text --- src/integrations/blocks/structured-data-blocks.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index 6782f35a7a6..a9ade61e111 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -176,6 +176,7 @@ public function optimize_faq_images( $attributes, $content ) { /** * Transforms the durations into a translated string containing the count, and either singular or plural unit. * For example (in en-US): If 'days' is 1, it returns "1 day". If 'days' is 2, it returns "2 days". + * If a number value is 0, we don't output the string. * * @param number $days Number of days. * @param number $hours Number of hours. @@ -184,21 +185,21 @@ public function optimize_faq_images( $attributes, $content ) { */ private function transform_duration_to_string( $days, $hours, $minutes ) { $strings = []; - if ( $days !== 0 ) { + if ( \intval( $days ) !== 0 ) { $strings[] = \sprintf( /* translators: %d expands to the number of day/days. */ \_n( '%d day', '%d days', $days, 'wordpress-seo' ), $days ); } - if ( $hours !== 0 ) { + if ( \intval( $hours ) !== 0 ) { $strings[] = \sprintf( /* translators: %d expands to the number of hour/hours. */ \_n( '%d hour', '%d hours', $hours, 'wordpress-seo' ), $hours ); } - if ( $minutes !== 0 ) { + if ( \intval( $minutes ) !== 0 ) { $strings[] = \sprintf( /* translators: %d expands to the number of minute/minutes. */ \_n( '%d minute', '%d minutes', $minutes, 'wordpress-seo' ), @@ -253,8 +254,11 @@ public function optimize_how_to_images( $attributes, $content ) { $duration = $this->build_duration_string( $attributes ); // 'Time needed:' is the default duration text that will be shown if a user doesn't add one. - $duration_text = ( ! $attributes['durationText'] ) ? ( \__( 'Time needed:', 'wordpress-seo' ) ) : ( $attributes['durationText'] ); - $content = \preg_replace( + $duration_text = \__( 'Time needed:', 'wordpress-seo' ); + if ( array_key_exists( 'durationText', $attributes ) && $attributes['durationText'] !== '' ) { + $duration_text = $attributes['durationText']; + } + $content = \preg_replace( '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/', '

' . $duration_text . ' ' . $duration . '

', $content, From b7d8ab7415a67e09981ab19cff0b510c9f209db8 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Mon, 3 Jul 2023 15:02:30 +0200 Subject: [PATCH 38/52] Add defensive coding --- src/integrations/blocks/structured-data-blocks.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index a9ade61e111..0140ee906ff 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -216,7 +216,10 @@ private function transform_duration_to_string( $days, $hours, $minutes ) { * @return string The formatted duration. */ private function build_duration_string( $attributes ) { - $elements = $this->transform_duration_to_string( $attributes['days'], $attributes['hours'], $attributes['minutes'] ); + $days = ( isset( $attributes['days'] ) ) ? ( $attributes['days'] ) : ( 0 ); + $hours = ( isset( $attributes['hours'] ) ) ? ( $attributes['hours'] ) : ( 0 ); + $minutes = ( isset( $attributes['minutes'] ) ) ? ( $attributes['minutes'] ) : ( 0 ); + $elements = $this->transform_duration_to_string( $days, $hours, $minutes ); $elements_length = count( $elements ); if ( $elements_length === 1 ) { @@ -255,9 +258,11 @@ public function optimize_how_to_images( $attributes, $content ) { $duration = $this->build_duration_string( $attributes ); // 'Time needed:' is the default duration text that will be shown if a user doesn't add one. $duration_text = \__( 'Time needed:', 'wordpress-seo' ); - if ( array_key_exists( 'durationText', $attributes ) && $attributes['durationText'] !== '' ) { + + if ( isset( $attributes['durationText'] ) && $attributes['durationText'] !== '' ) { $duration_text = $attributes['durationText']; } + $content = \preg_replace( '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/', '

' . $duration_text . ' ' . $duration . '

', From 962144494f7740211d37f0139bb5aa00b41bc541 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Mon, 3 Jul 2023 16:55:45 +0200 Subject: [PATCH 39/52] Split the function --- .../blocks/structured-data-blocks.php | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index 0140ee906ff..40118035cb3 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -243,18 +243,14 @@ private function build_duration_string( $attributes ) { } /** - * Optimizes images in the How-To blocks. + * Presents the duration text of the How-To block in the site language. * * @param array $attributes The attributes. * @param string $content The content. * - * @return string The content with images optimized. + * @return string The content with the duration text in the site language. */ - public function optimize_how_to_images( $attributes, $content ) { - if ( ! isset( $attributes['steps'] ) ) { - return $content; - } - + public function present_duration_text( $attributes, $content ) { $duration = $this->build_duration_string( $attributes ); // 'Time needed:' is the default duration text that will be shown if a user doesn't add one. $duration_text = \__( 'Time needed:', 'wordpress-seo' ); @@ -263,12 +259,28 @@ public function optimize_how_to_images( $attributes, $content ) { $duration_text = $attributes['durationText']; } - $content = \preg_replace( + return \preg_replace( '/(

)(.*<\/span>)(.[^\/p>]*)(<\/p>)/', '

' . $duration_text . ' ' . $duration . '

', $content, 1 ); + } + + /** + * Optimizes images in the How-To blocks. + * + * @param array $attributes The attributes. + * @param string $content The content. + * + * @return string The content with images optimized. + */ + public function optimize_how_to_images( $attributes, $content ) { + if ( ! isset( $attributes['steps'] ) ) { + return $content; + } + + $content = $this->present_duration_text( $attributes, $content ); return $this->optimize_images( $attributes['steps'], 'text', $content ); } From ea4592a0955331506b6f5db130accada8b370611 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 4 Jul 2023 09:44:18 +0200 Subject: [PATCH 40/52] Add initial implementation of the unit tests --- .../blocks/structured-data-blocks-test.php | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 tests/unit/integrations/blocks/structured-data-blocks-test.php diff --git a/tests/unit/integrations/blocks/structured-data-blocks-test.php b/tests/unit/integrations/blocks/structured-data-blocks-test.php new file mode 100644 index 00000000000..a28b526068e --- /dev/null +++ b/tests/unit/integrations/blocks/structured-data-blocks-test.php @@ -0,0 +1,125 @@ +asset_manager = Mockery::mock( WPSEO_Admin_Asset_Manager::class ); + $this->image_helper = Mockery::mock( Image_Helper::class ); + + $this->instance = new Structured_Data_Blocks( + $this->asset_manager, + $this->image_helper + ); + } + + /** + * Tests __construct method. + * + * @covers ::__construct + */ + public function test_construct() { + static::assertInstanceOf( + Structured_Data_Blocks::class, + new Structured_Data_Blocks( + $this->asset_manager, + $this->image_helper + ) + ); + } + + /** + * Data provider for the test_optimize_how_to_images method. + * + * @return array[] + */ + public function how_to_block_provider() { + return [ + [ + '

The amount of time it will take: 1 hour and 20 minutes

', + [ + 'durationText' => 'The amount of time it will take:', + 'defaultDurationText' => 'Time needed:', + 'days' => '0', + 'hours' => '1', + 'minutes' => '20', + ], + '

Time needed: 1 hour and 20 minutes

', + 'A test case for when the non-default duration text is available', + ], + [ + '

Time needed: 1 hour and 20 minutes

', + [ + 'defaultDurationText' => 'Time needed:', + 'days' => '0', + 'hours' => '1', + 'minutes' => '20', + ], + '

Time needed: 1 hour and 20 minutes

', + 'A test case for when the non-default duration text is not available', + ], + ]; + } + + /** + * Tests that present returns the expected content. + * + * @covers ::present_duration_text + * @dataProvider how_to_block_provider + * + * @param string $expected The expected content. + * @param array $attributes The block attributes. + * @param string $content The post content. + * @param string $message The error message if the assert fails. + */ + public function test_present_duration_text( $expected, $attributes, $content, $message ) { + $this->assertSame( + $expected, + $this->instance->present_duration_text( + $attributes, + $content + ), + $message + ); + } +} From 46a93df5265e2e2c31f8ce69e740109ce69ac009 Mon Sep 17 00:00:00 2001 From: Martijn van der Klis Date: Tue, 4 Jul 2023 13:35:17 +0200 Subject: [PATCH 41/52] Adds comma after "for example" --- packages/js/src/settings/routes/crawl-optimization.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/src/settings/routes/crawl-optimization.js b/packages/js/src/settings/routes/crawl-optimization.js index eb5cb318f37..a45f4d40fe2 100644 --- a/packages/js/src/settings/routes/crawl-optimization.js +++ b/packages/js/src/settings/routes/crawl-optimization.js @@ -234,7 +234,7 @@ const CrawlOptimization = () => { sprintf( /** * translators: - * %1$s through %7$s each expand to a parameter name within a tag. For example gclid. + * %1$s through %7$s each expand to a parameter name within a tag. For example, gclid. */ __( "Note that the following commonly-used parameters will not be removed: %1$s, %2$s, %3$s, %4$s, %5$s, %6$s, and %7$s.", "wordpress-seo" ), From a889f93aac4835e6ff15256d15d0a689f7d9114d Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 4 Jul 2023 14:37:32 +0200 Subject: [PATCH 42/52] Adjust unit tests --- .../blocks/structured-data-blocks-test.php | 60 ++++++++++++++++--- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/tests/unit/integrations/blocks/structured-data-blocks-test.php b/tests/unit/integrations/blocks/structured-data-blocks-test.php index a28b526068e..13080f9c7cd 100644 --- a/tests/unit/integrations/blocks/structured-data-blocks-test.php +++ b/tests/unit/integrations/blocks/structured-data-blocks-test.php @@ -44,6 +44,8 @@ class Structured_Data_Blocks_Test extends TestCase { protected function set_up() { parent::set_up(); + $this->stubTranslationFunctions(); + $this->asset_manager = Mockery::mock( WPSEO_Admin_Asset_Manager::class ); $this->image_helper = Mockery::mock( Image_Helper::class ); @@ -76,28 +78,70 @@ public function test_construct() { public function how_to_block_provider() { return [ [ - '

The amount of time it will take: 1 hour and 20 minutes

', + '

The amount of time it will take: 2 hours and 20 minutes

', [ 'durationText' => 'The amount of time it will take:', 'defaultDurationText' => 'Time needed:', - 'days' => '0', - 'hours' => '1', - 'minutes' => '20', + 'hours' => 2, + 'minutes' => 20, ], - '

Time needed: 1 hour and 20 minutes

', + '

Time needed: 2 hours and 20 minutes

', 'A test case for when the non-default duration text is available', ], [ '

Time needed: 1 hour and 20 minutes

', [ 'defaultDurationText' => 'Time needed:', - 'days' => '0', - 'hours' => '1', - 'minutes' => '20', + 'hours' => 1, + 'minutes' => 20, ], '

Time needed: 1 hour and 20 minutes

', 'A test case for when the non-default duration text is not available', ], + [ + '

Time needed: 2 days, 1 hour and 20 minutes

', + [ + 'defaultDurationText' => 'Time needed:', + 'days' => 2, + 'hours' => 1, + 'minutes' => 20, + ], + '

Time needed: 2 days, 1 hour and 20 minutes

', + 'A test case for when the time units for days, hours and minutes are available', + ], + [ + '

Time needed: 3 hours

', + [ + 'defaultDurationText' => 'Time needed:', + 'days' => 0, + 'hours' => 3, + 'minutes' => 0, + ], + '

Time needed: 3 hours

', + 'A test case for when the time units are only available for hours', + ], + [ + '

Time needed: 45 minutes

', + [ + 'defaultDurationText' => 'Time needed:', + 'days' => 0, + 'hours' => 0, + 'minutes' => 45, + ], + '

Time needed: 45 minutes

', + 'A test case for when the time units are only available for minutes', + ], + [ + '

The Norwegian Forest cat (Norwegian: Norsk skogskatt and Norsk skaukatt) is a breed of domestic cat originating in Northern Europe.', + [ + 'defaultDurationText' => 'Time needed:', + 'days' => 0, + 'hours' => 0, + 'minutes' => 0, + ], + '

The Norwegian Forest cat (Norwegian: Norsk skogskatt and Norsk skaukatt) is a breed of domestic cat originating in Northern Europe.', + 'A test case for when the element with "schema-how-to-total-time" class name is not output in the content', + ], ]; } From 396527577f3d171e954db70ca6baa88d413a6cfd Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Tue, 4 Jul 2023 14:39:34 +0200 Subject: [PATCH 43/52] Fix comment --- tests/unit/integrations/blocks/structured-data-blocks-test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/integrations/blocks/structured-data-blocks-test.php b/tests/unit/integrations/blocks/structured-data-blocks-test.php index 13080f9c7cd..4f02e0d8143 100644 --- a/tests/unit/integrations/blocks/structured-data-blocks-test.php +++ b/tests/unit/integrations/blocks/structured-data-blocks-test.php @@ -71,7 +71,7 @@ public function test_construct() { } /** - * Data provider for the test_optimize_how_to_images method. + * Data provider for the present_duration_text method. * * @return array[] */ From 9c77ac9bba1228943462b3c9b70c54449d290e5e Mon Sep 17 00:00:00 2001 From: Marina Koleva Date: Tue, 4 Jul 2023 15:44:43 +0200 Subject: [PATCH 44/52] updating link to google search console page --- packages/js/src/settings/routes/site-connections.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/src/settings/routes/site-connections.js b/packages/js/src/settings/routes/site-connections.js index c0cd51c4312..be93ad2fe92 100644 --- a/packages/js/src/settings/routes/site-connections.js +++ b/packages/js/src/settings/routes/site-connections.js @@ -90,7 +90,7 @@ const SiteConnections = () => { "", "" ), - addQueryArgs( "https://www.google.com/webmasters/verification/verification", { hl: "en", tid: "alternate", siteUrl } ), + addQueryArgs( "https://search.google.com/search-console/users", { hl: "en", tid: "alternate", siteUrl } ), "link-google-search-console" ) } placeholder={ __( "Add verification code", "wordpress-seo" ) } From 1de4acb1f68b70057a9f79c5b08944b0ffbbe655 Mon Sep 17 00:00:00 2001 From: Aida Marfuaty <48715883+FAMarfuaty@users.noreply.github.com> Date: Thu, 6 Jul 2023 11:22:44 +0200 Subject: [PATCH 45/52] Simplify check Co-authored-by: Vraja Das <65466507+vraja-pro@users.noreply.github.com> --- src/integrations/blocks/structured-data-blocks.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index 40118035cb3..168e09737d8 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -185,21 +185,21 @@ public function optimize_faq_images( $attributes, $content ) { */ private function transform_duration_to_string( $days, $hours, $minutes ) { $strings = []; - if ( \intval( $days ) !== 0 ) { + if ( $days ) { $strings[] = \sprintf( /* translators: %d expands to the number of day/days. */ \_n( '%d day', '%d days', $days, 'wordpress-seo' ), $days ); } - if ( \intval( $hours ) !== 0 ) { + if ( $hours ) { $strings[] = \sprintf( /* translators: %d expands to the number of hour/hours. */ \_n( '%d hour', '%d hours', $hours, 'wordpress-seo' ), $hours ); } - if ( \intval( $minutes ) !== 0 ) { + if ( $minutes ) { $strings[] = \sprintf( /* translators: %d expands to the number of minute/minutes. */ \_n( '%d minute', '%d minutes', $minutes, 'wordpress-seo' ), From 32ea877f9b6eb2d2720c7bd70046db2386a6e58d Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Thu, 6 Jul 2023 11:43:11 +0200 Subject: [PATCH 46/52] Simplify code --- .../blocks/structured-data-blocks.php | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index 40118035cb3..a8fc565939d 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -185,21 +185,21 @@ public function optimize_faq_images( $attributes, $content ) { */ private function transform_duration_to_string( $days, $hours, $minutes ) { $strings = []; - if ( \intval( $days ) !== 0 ) { + if ( $days ) { $strings[] = \sprintf( /* translators: %d expands to the number of day/days. */ \_n( '%d day', '%d days', $days, 'wordpress-seo' ), $days ); } - if ( \intval( $hours ) !== 0 ) { + if ( $hours ) { $strings[] = \sprintf( /* translators: %d expands to the number of hour/hours. */ \_n( '%d hour', '%d hours', $hours, 'wordpress-seo' ), $hours ); } - if ( \intval( $minutes ) !== 0 ) { + if ( $minutes ) { $strings[] = \sprintf( /* translators: %d expands to the number of minute/minutes. */ \_n( '%d minute', '%d minutes', $minutes, 'wordpress-seo' ), @@ -216,30 +216,30 @@ private function transform_duration_to_string( $days, $hours, $minutes ) { * @return string The formatted duration. */ private function build_duration_string( $attributes ) { - $days = ( isset( $attributes['days'] ) ) ? ( $attributes['days'] ) : ( 0 ); - $hours = ( isset( $attributes['hours'] ) ) ? ( $attributes['hours'] ) : ( 0 ); - $minutes = ( isset( $attributes['minutes'] ) ) ? ( $attributes['minutes'] ) : ( 0 ); + $days = ( $attributes['days'] ?? 0 ); + $hours = ( $attributes['hours'] ?? 0 ); + $minutes = ( $attributes['minutes'] ?? 0 ); $elements = $this->transform_duration_to_string( $days, $hours, $minutes ); $elements_length = count( $elements ); - if ( $elements_length === 1 ) { - return $elements[0]; - } - if ( $elements_length === 2 ) { - return \sprintf( - /* translators: %s expands to a unit of time (e.g. 1 day). */ - \__( '%1$s and %2$s', 'wordpress-seo' ), - ...$elements - ); - } - if ( $elements_length === 3 ) { - return \sprintf( + switch ( $elements_length ) { + case 1: + return $elements[0]; + case 2: + return \sprintf( + /* translators: %s expands to a unit of time (e.g. 1 day). */ + \__( '%1$s and %2$s', 'wordpress-seo' ), + ...$elements + ); + case 3: + return \sprintf( /* translators: %s expands to a unit of time (e.g. 1 day). */ - \__( '%1$s, %2$s and %3$s', 'wordpress-seo' ), - ...$elements - ); + \__( '%1$s, %2$s and %3$s', 'wordpress-seo' ), + ...$elements + ); + default: + return ''; } - return ''; } /** From b984098d9724402886963ec777ac1e983a6fa216 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Thu, 6 Jul 2023 11:45:49 +0200 Subject: [PATCH 47/52] fix indentation --- src/integrations/blocks/structured-data-blocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integrations/blocks/structured-data-blocks.php b/src/integrations/blocks/structured-data-blocks.php index a8fc565939d..32b464c2475 100644 --- a/src/integrations/blocks/structured-data-blocks.php +++ b/src/integrations/blocks/structured-data-blocks.php @@ -233,7 +233,7 @@ private function build_duration_string( $attributes ) { ); case 3: return \sprintf( - /* translators: %s expands to a unit of time (e.g. 1 day). */ + /* translators: %s expands to a unit of time (e.g. 1 day). */ \__( '%1$s, %2$s and %3$s', 'wordpress-seo' ), ...$elements ); From c66ca8ef03d64bac18212caeeb43a113106b79fa Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Thu, 6 Jul 2023 13:18:00 +0200 Subject: [PATCH 48/52] add unit test --- .../blocks/structured-data-blocks-test.php | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/unit/integrations/blocks/structured-data-blocks-test.php b/tests/unit/integrations/blocks/structured-data-blocks-test.php index 4f02e0d8143..c6edf5e038d 100644 --- a/tests/unit/integrations/blocks/structured-data-blocks-test.php +++ b/tests/unit/integrations/blocks/structured-data-blocks-test.php @@ -166,4 +166,64 @@ public function test_present_duration_text( $expected, $attributes, $content, $m $message ); } + + /** + * Data provider for the present_duration_text method. + * + * @return array[] + */ + public function how_to_image_provider() { + return [ + [ + '

The amount of time it will take: 2 hours and 20 minutes

' . + '
  1. ' . + 'Step 1

    Do this step

    ' . + '
  2. ' . + 'Step 2

    Do those steps

', + [ + 'durationText' => 'The amount of time it will take:', + 'defaultDurationText' => 'Time needed:', + 'hours' => 2, + 'minutes' => 20, + 'steps' => [ + [ + 'id' => 'how-to-step-1688388022851', + 'name' => '', + 'text' => 'Step 1', + 'jsonName' => 'Step 1', + 'jsonText' => 'Do this step', + ], + ], + ], + '

The amount of time it will take: 2 hours and 20 minutes

' . + '
  1. ' . + 'Step 1

    Do this step

    ' . + '
  2. ' . + 'Step 2

    Do those steps

', + 'A test case for when there is no image in the block', + ], + ]; + } + + /** + * Tests that present returns the expected content. + * + * @covers ::optimize_how_to_images + * @dataProvider how_to_image_provider + * + * @param string $expected The expected content. + * @param array $attributes The block attributes. + * @param string $content The post content. + * @param string $message The error message if the assert fails. + */ + public function test_optimize_how_to_images( $expected, $attributes, $content, $message ) { + $this->assertSame( + $expected, + $this->instance->optimize_how_to_images( + $attributes, + $content + ), + $message + ); + } } From bbdaf0703907fdb25d204d280cb6e5e279467dcf Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Thu, 6 Jul 2023 13:25:38 +0200 Subject: [PATCH 49/52] fix typo --- .../integrations/blocks/structured-data-blocks-test.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/integrations/blocks/structured-data-blocks-test.php b/tests/unit/integrations/blocks/structured-data-blocks-test.php index c6edf5e038d..977dc53dc63 100644 --- a/tests/unit/integrations/blocks/structured-data-blocks-test.php +++ b/tests/unit/integrations/blocks/structured-data-blocks-test.php @@ -168,11 +168,11 @@ public function test_present_duration_text( $expected, $attributes, $content, $m } /** - * Data provider for the present_duration_text method. + * Data provider for the optimize_how_to_images method. * * @return array[] */ - public function how_to_image_provider() { + public function how_to_images_provider() { return [ [ '

The amount of time it will take: 2 hours and 20 minutes

' . @@ -209,7 +209,7 @@ public function how_to_image_provider() { * Tests that present returns the expected content. * * @covers ::optimize_how_to_images - * @dataProvider how_to_image_provider + * @dataProvider how_to_images_provider * * @param string $expected The expected content. * @param array $attributes The block attributes. From afc14086ccef28f9d95a408aee2ea6a5c11afe8a Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Thu, 6 Jul 2023 15:14:33 +0200 Subject: [PATCH 50/52] Mock a method --- .../unit/integrations/blocks/structured-data-blocks-test.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit/integrations/blocks/structured-data-blocks-test.php b/tests/unit/integrations/blocks/structured-data-blocks-test.php index 977dc53dc63..89bb1bd59a8 100644 --- a/tests/unit/integrations/blocks/structured-data-blocks-test.php +++ b/tests/unit/integrations/blocks/structured-data-blocks-test.php @@ -3,6 +3,7 @@ namespace Yoast\WP\SEO\Tests\Unit\Integrations\Blocks; use Mockery; +use Brain\Monkey; use Yoast\WP\SEO\Integrations\Blocks\Structured_Data_Blocks; use WPSEO_Admin_Asset_Manager; use Yoast\WP\SEO\Helpers\Image_Helper; @@ -217,6 +218,9 @@ public function how_to_images_provider() { * @param string $message The error message if the assert fails. */ public function test_optimize_how_to_images( $expected, $attributes, $content, $message ) { + Monkey\Functions\expect( 'update_post_meta' ) + ->andReturn( true ); + $this->assertSame( $expected, $this->instance->optimize_how_to_images( From 2f63bd9bac35240dfc3f80971d4ee0c38bbdc6d3 Mon Sep 17 00:00:00 2001 From: aidamarfuaty Date: Thu, 6 Jul 2023 16:48:02 +0200 Subject: [PATCH 51/52] Mock a method --- tests/unit/integrations/blocks/structured-data-blocks-test.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/integrations/blocks/structured-data-blocks-test.php b/tests/unit/integrations/blocks/structured-data-blocks-test.php index 89bb1bd59a8..9bfd9c2e2be 100644 --- a/tests/unit/integrations/blocks/structured-data-blocks-test.php +++ b/tests/unit/integrations/blocks/structured-data-blocks-test.php @@ -218,7 +218,8 @@ public function how_to_images_provider() { * @param string $message The error message if the assert fails. */ public function test_optimize_how_to_images( $expected, $attributes, $content, $message ) { - Monkey\Functions\expect( 'update_post_meta' ) + Monkey\Functions\expect( 'register_shutdown_function' ) + ->withAnyArgs() ->andReturn( true ); $this->assertSame( From 7cbf45cc56456f2c45136f7a735153a3401a2381 Mon Sep 17 00:00:00 2001 From: hdvos Date: Fri, 7 Jul 2023 15:39:10 +0200 Subject: [PATCH 52/52] add rel="noreferrer" to link --- packages/components/src/WordOccurrenceInsights.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/WordOccurrenceInsights.js b/packages/components/src/WordOccurrenceInsights.js index bb96f2f7ee4..6de3e4cfa7f 100644 --- a/packages/components/src/WordOccurrenceInsights.js +++ b/packages/components/src/WordOccurrenceInsights.js @@ -32,7 +32,7 @@ const getKeywordResearchArticleLink = ( url ) => { mixedString: keywordsResearchLinkTranslation, components: { // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-no-target-blank - a: , + a: , }, } ); };