Skip to content
This repository was archived by the owner on Oct 4, 2022. It is now read-only.

P1 345 woo shopping data in google preview #1118

Merged
merged 25 commits into from
Apr 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1a1cf3d
introduce StarRating component
marijnyoast Mar 3, 2021
7109bf8
Introduce ProductData components
marijnyoast Mar 16, 2021
bb1398e
pass shoppingData to Preview
marijnyoast Mar 16, 2021
96d74c3
update export
marijnyoast Mar 16, 2021
b279617
fix typo
marijnyoast Mar 16, 2021
7046bee
add translations
marijnyoast Mar 17, 2021
b01ce11
remove non-functioning stylesheet
marijnyoast Mar 17, 2021
9eacdd4
Restructure and styling
marijnyoast Mar 22, 2021
0b14ad5
fix props existency
marijnyoast Mar 23, 2021
6029dc2
update snapshot
marijnyoast Mar 23, 2021
b9ab211
Update snaps
maartenleenders Mar 24, 2021
b676e93
Merge branch 'develop' into P1-345-Woo-shopping-data-in-google-preview
marijnyoast Mar 24, 2021
cfbb1b9
remove faulty snapshot tests
marijnyoast Mar 24, 2021
d678bad
Merge branch 'P1-345-Woo-shopping-data-in-google-preview' of https://…
marijnyoast Mar 24, 2021
1efba0e
CS fixes in test
marijnyoast Mar 24, 2021
7cf656e
remove even more snapshot testing
marijnyoast Mar 25, 2021
505e6c8
remove obsolete snapshots
marijnyoast Mar 25, 2021
fb4ca07
limit rating input between 0 and 5
marijnyoast Apr 1, 2021
987d696
update translateability and replace ternary's
marijnyoast Apr 6, 2021
74e8ba7
define PropType shape
marijnyoast Apr 6, 2021
e487a15
simplify existence check
marijnyoast Apr 6, 2021
bc4cba5
fix typo
marijnyoast Apr 6, 2021
c98f93e
Apply suggestions from code review
marijnyoast Apr 7, 2021
f904659
fix object empty check
marijnyoast Apr 7, 2021
0d8b89d
Merge branch 'P1-345-Woo-shopping-data-in-google-preview' of https://…
marijnyoast Apr 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion apps/components/ComponentsExample.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { __ } from "@wordpress/i18n";

import { Alert, CourseDetails, FullHeightCard, Warning } from "@yoast/components";
import { Alert, CourseDetails, FullHeightCard, StarRating, Warning } from "@yoast/components";
import { getCourseFeed, getDirectionalStyle, makeOutboundLink } from "@yoast/helpers";
import React from "react";
import styled from "styled-components";
Expand Down Expand Up @@ -138,6 +138,12 @@ export default class ComponentsExample extends React.Component {
</Alert>;
}

updateStars( event ) {
this.setState({
input: event.target.value
} );
}

/**
* Renders all the Component examples.
*
Expand Down Expand Up @@ -171,6 +177,9 @@ export default class ComponentsExample extends React.Component {
<p key="2">This spans to multiple lines.</p>,
] }
/>
<h2>Star rating</h2>
<i>Accepts a rating from 0-5 and colors the stars yellow accordingly</i>
<StarRating rating={ 3.5 } />
<h2>Outbound links</h2>
<p>
<NonYoastLink href="http://www.example.org">example.org</NonYoastLink>
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from "./inputs";
export * from "./insights-card";
export * from "./radiobutton";
export * from "./select";
export * from "./star-rating";
export * from "./help-icon";
export * from "./tables";

Expand Down
41 changes: 41 additions & 0 deletions packages/components/src/star-rating/StarRating.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";
import PropTypes from "prop-types";

/**
* Renders StarRating component.
*
* @param {Object} props The props.
*
* @returns {React.Component} The StarRating Component displays a number between 0 and 5 as (partly) colored stars.
*/
function StarRating( props ) {
let rating = props.rating;

// As we have 5 stars, the rating should be between 0 and 5.
if ( rating < 0 ) {
rating = 0;
}

if ( rating > 5 ) {
rating = 5;
}

const ratingPercentage = rating * 20;

return (
<div
aria-hidden="true"
className="yoast-star-rating"
>
<span className="yoast-star-rating__placeholder" role="img">
<span className="yoast-star-rating__fill" style={ { width: ratingPercentage + "%" } } />
</span>
</div>
);
}

export default StarRating;

StarRating.propTypes = {
rating: PropTypes.number.isRequired,
};
3 changes: 3 additions & 0 deletions packages/components/src/star-rating/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import "./star-rating.css";

export { default as StarRating } from "./StarRating.js";
24 changes: 24 additions & 0 deletions packages/components/src/star-rating/star-rating.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.yoast-star-rating {
width: 65px;
height: 12px;
display: inline-block;
}

.yoast-star-rating span {
height: 100%;
width: 100%;
background-size: 13px 12px;
background-repeat: repeat-x;
}

.yoast-star-rating__placeholder {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAmCAQAAAAYCMGrAAAA+klEQVR4AcWV4cbtMBBFF0MIVUopoVSrhDDv/3gf/RFRpzdNOty1HiBO99mzeYWgCMZMKCPGrCgrxiSUhCkDeukxJKCXAUMiehkxw6FZhxEzmp0x4kCzByYISqlYdal0supS6WrVpdLEK0YSamJiJOPY0c/uOG4s6CcXfuKJaJcRzyNCQJsNiF1sRTR1hP11NNJ8RCrONOPRf+r7J+TZgQ5CNfMOYvW/2YxDqzqA/57+gVY9eiakrnyZEGXDsaE3p/4JScwPX3rtnZATDxnPWT7X16XAHaH8HWNrlxJD9TyGti5tCM84zpZe+RxNjeX9tZqLaGoMxN/P/wHP5Vw+8ZxnEQAAAABJRU5ErkJggg==);
display: inline-block;
overflow: hidden;
position: relative;
}

.yoast-star-rating__fill {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAmBAMAAABALxQTAAAAFVBMVEVMaXH4twP4twP4twP4twP4twP4twP7w8S/AAAAB3RSTlMAFv5uPpvQloUsTQAAAMFJREFUeAGE0TEOgzAMQFEXoDNiYC6/wFxxAsTADDkB5f6HqNRENXUi8TYiRfnY8lNXkjBOkuBWSeAhsYJOYiW9xO4MEqshkTbCSyIH7GLdgFasHHgmwkikZQD6OROZRG4Hxju8o/TNhbNhCqkOxaZDVKdxNnq/EjUS/A2o0PuXpyVeb9bjDWY9QSWXDQfBbtbjtWY9bM4sqfx+5yYt8wNcAFEzrGGkk5668KsFrKewPtQ3aFqh8WOnYZ+lIBQkgykAWk8rlAqcHfQAAAAASUVORK5CYII=);
display: block;
}
2 changes: 1 addition & 1 deletion packages/search-metadata-previews/src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Snippet preview exports.
export { default as FixedWidthContainer } from "./snippet-preview/FixedWidthContainer";
export { default as HelpTextWrapper } from "./snippet-preview/HelpTextWrapper";
export { default as SnippetPreview } from "./snippet-preview/SnippetPreview";
export * from "./snippet-preview/SnippetPreview";

// Snippet editor exports.
export {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ class SnippetEditor extends React.Component {
faviconSrc,
mobileImageSrc,
idSuffix,
shoppingData,
} = this.props;

const {
Expand Down Expand Up @@ -556,6 +557,7 @@ class SnippetEditor extends React.Component {
locale={ locale }
faviconSrc={ faviconSrc }
mobileImageSrc={ mobileImageSrc }
shoppingData={ shoppingData }
{ ...mappedData }
/>

Expand Down Expand Up @@ -601,6 +603,7 @@ SnippetEditor.propTypes = {
faviconSrc: PropTypes.string,
mobileImageSrc: PropTypes.string,
idSuffix: PropTypes.string,
shoppingData: PropTypes.object,
};

SnippetEditor.defaultProps = {
Expand Down Expand Up @@ -629,6 +632,7 @@ SnippetEditor.defaultProps = {
faviconSrc: "",
mobileImageSrc: "",
idSuffix: "",
shoppingData: {},
};

export default SnippetEditor;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { Fragment } from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import { __, sprintf } from "@wordpress/i18n";
import { round } from "lodash";
import { StarRating } from "@yoast/components";

const ProductData = styled.span`
color: #70757a;
`;

/**
* Renders ProductData component.
*
* @param {Object} props The props.
*
* @returns {React.Component} The StarRating Component.
*/
function ProductDataDesktop( props ) {
const { shoppingData } = props;

/* Translators: %s expands to the actual rating, e.g. 8/10. */
const ratingPart = sprintf( __( "Rating: %s", "yoast-components" ), round( ( shoppingData.rating * 2 ), 1 ) + "/10" );

/* Translators: %s expands to the review count. */
const reviewPart = sprintf( __( "%s reviews", "yoast-components" ), shoppingData.reviewCount );

return (
<ProductData>
{ ( shoppingData.reviewCount > 0 ) &&
<Fragment>
<StarRating rating={ shoppingData.rating } />
<span> { ratingPart } · </span>
<span>{ reviewPart } · </span>
</Fragment>
}
{ shoppingData.price &&
<Fragment>
<span dangerouslySetInnerHTML={ { __html: shoppingData.price } } />‎
</Fragment>
}
{ shoppingData.availability &&
<span> · { shoppingData.availability }</span> }
</ProductData>
);
}

export default ProductDataDesktop;

ProductDataDesktop.propTypes = {
shoppingData: PropTypes.shape( {
rating: PropTypes.number,
reviewCount: PropTypes.number,
availability: PropTypes.string,
price: PropTypes.string,
} ).isRequired,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import { __ } from "@wordpress/i18n";
import { round } from "lodash";

import { StarRating } from "@yoast/components";

const ProductData = styled.div`
display: flex;
`;

const ProductDataCell50 = styled.div`
flex: 1;
max-width: 50%;
`;

const ProductDataCell25 = styled.div`
flex: 1;
max-width: 25%;
`;

const ProductDataInnerLower = styled.div`
color: #70757a;
`;

/**
* Renders ProductData component.
*
* @param {Object} props The props.
*
* @returns {React.Component} The StarRating Component.
*/
function ProductDataMobile( props ) {
const { shoppingData } = props;

return (
<ProductData>
{ ( shoppingData.rating > 0 ) &&
<ProductDataCell50 className="yoast-shopping-data-preview__column">
<div className="yoast-shopping-data-preview__upper">{ __( "Rating", "yoast-components" ) }</div>
<ProductDataInnerLower className="yoast-shopping-data-preview__lower">
<span>{ round( ( shoppingData.rating * 2 ), 1 ) }/10 </span>
<StarRating rating={ shoppingData.rating } />
<span> ({ shoppingData.reviewCount })</span>
</ProductDataInnerLower>
</ProductDataCell50>
}
{ ( shoppingData.price ) &&
<ProductDataCell25 className="yoast-shopping-data-preview__column">
<div className="yoast-shopping-data-preview__upper">{ __( "Price", "yoast-components" ) }</div>
<ProductDataInnerLower
className="yoast-shopping-data-preview__lower"
dangerouslySetInnerHTML={ { __html: shoppingData.price } }
/>
</ProductDataCell25>
}
{ ( shoppingData.availability ) &&
<ProductDataCell25 className="yoast-shopping-data-preview__column">
<div className="yoast-shopping-data-preview__upper">{ __( "Availability", "yoast-components" ) }</div>
<ProductDataInnerLower className="yoast-shopping-data-preview__lower">
{ shoppingData.availability }
</ProductDataInnerLower>
</ProductDataCell25>
}
</ProductData>
);
}

export default ProductDataMobile;

ProductDataMobile.propTypes = {
shoppingData: PropTypes.shape( {
rating: PropTypes.number,
reviewCount: PropTypes.number,
availability: PropTypes.string,
price: PropTypes.string,
} ).isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const {

// Internal dependencies.
import FixedWidthContainer from "./FixedWidthContainer";
import ProductDataDesktop from "./ProductDataDesktop";
import ProductDataMobile from "./ProductDataMobile";
import { DEFAULT_MODE, MODE_DESKTOP, MODE_MOBILE, MODES } from "./constants";

/*
Expand Down Expand Up @@ -722,6 +724,37 @@ export default class SnippetPreview extends PureComponent {
return null;
}

/**
* Renders the product / shopping data, in mobile or desktop view, based on the mode.
*
* @returns {ReactElement} The rendered description.
*/
renderProductData() {
const { mode, shoppingData } = this.props;

if ( Object.values( shoppingData ).length === 0 ) {
return null;
}

if ( mode === MODE_DESKTOP ) {
return (
<ProductDataDesktop
shoppingData={ shoppingData }
/>
);
}

if ( mode === MODE_MOBILE ) {
return (
<ProductDataMobile
shoppingData={ shoppingData }
/>
);
}

return null;
}

/**
* Renders the snippet preview.
*
Expand Down Expand Up @@ -783,12 +816,24 @@ export default class SnippetPreview extends PureComponent {
</SnippetTitle>
{ amp }
</PartContainer>
<PartContainer className="yoast-shopping-data-preview--desktop">
<ScreenReaderText>
{ __( "Shopping data preview:", "yoast-components" ) }
</ScreenReaderText>
{ isDesktopMode && this.renderProductData() }
</PartContainer>
<PartContainer>
<ScreenReaderText>
{ __( "Meta description preview", "yoast-components" ) + ":" }
{ __( "Meta description preview:", "yoast-components" ) }
</ScreenReaderText>
{ this.renderDescription() }
</PartContainer>
<PartContainer className="yoast-shopping-data-preview--mobile">
<ScreenReaderText>
{ __( "Shopping data preview:", "yoast-components" ) }
</ScreenReaderText>
{ ! isDesktopMode && this.renderProductData() }
</PartContainer>
</Container>
</section>
);
Expand Down Expand Up @@ -837,6 +882,7 @@ SnippetPreview.propTypes = {
isAmp: PropTypes.bool,
faviconSrc: PropTypes.string,
mobileImageSrc: PropTypes.string,
shoppingData: PropTypes.object,

onMouseUp: PropTypes.func.isRequired,
onHover: PropTypes.func,
Expand All @@ -856,6 +902,7 @@ SnippetPreview.defaultProps = {
isAmp: false,
faviconSrc: "",
mobileImageSrc: "",
shoppingData: {},

onHover: () => {},
onMouseEnter: () => {},
Expand Down
Loading