Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace microdata to json-ld #4018

Merged
merged 8 commits into from Feb 11, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add `Intl.NumberFormat()`/`toLocaleString()` via polyfill support in NodeJs - @cewald (#3836, #4040)
- Added `saveBandwidthOverCache` parameter for skipping caching for products data - @andrzejewsky (#3706)
- New zoom effect for product gallery images - @Michal-Dziedzinski (#2755)
- Product Page Schema implementation as JSON-LD - @Michal-Dziedzinski (#3704)

### Fixed
- Fixed Search product fails for category filter when categoryId is string - @adityasharma7 (#3929)
Expand Down
94 changes: 94 additions & 0 deletions core/helpers/index.ts
Expand Up @@ -253,3 +253,97 @@ export function extendStore (moduleName: string | string[], module: any) {
store.unregisterModule(moduleName)
store.registerModule(moduleName, extendedModule)
}

export function reviewJsonLd (reviews, {name, category, mpn, url_path, price, stock, is_in_stock, sku, image, description}, priceCurrency) {
return reviews.map(({title, detail, nickname, created_at}) => (
{
'@context': 'http://schema.org/',
'@type': 'Review',
reviewAspect: title,
reviewBody: detail,
datePublished: created_at,
author: nickname,
itemReviewed: {
'@type': 'Product',
name,
sku,
image,
description,
offers: {
'@type': 'Offer',
category: category
? category
.map(({ name }) => name || null)
.filter(name => name !== null)
: null,
mpn,
url: url_path,
priceCurrency,
price,
itemCondition: 'https://schema.org/NewCondition',
availability: stock && is_in_stock ? 'InStock' : 'OutOfStock'
}
}
}
)
)
}

function getMaterials (material, customAttributes) {
const materialsArr = []
if (customAttributes && customAttributes.length && customAttributes.length > 0 && material && material.length && material.length > 0) {
const materialOptions = customAttributes.find(({attribute_code}) => attribute_code === 'material').options
if (Array.isArray(material)) {
for (let key in materialOptions) {
material.forEach(el => {
if (String(el) === materialOptions[key].value) {
materialsArr.push(materialOptions[key].label)
}
})
}
} else {
for (let key in materialOptions) {
if (material === materialOptions[key].value) {
materialsArr.push(materialOptions[key].label)
}
}
}
}
return materialsArr
}

export function productJsonLd ({ category, image, name, id, sku, mpn, description, price, url_path, stock, is_in_stock, material }, color, priceCurrency, customAttributes) {
return {
'@context': 'http://schema.org',
'@type': 'Product',
category: category
? category
.map(({ name }) => name || null)
.filter(name => name !== null)
: null,
color,
description,
image,
itemCondition: 'http://schema.org/NewCondition',
material: getMaterials(material, customAttributes),
name,
productID: id,
sku,
mpn,
offers: {
'@type': 'Offer',
category: category
? category
.map(({ name }) => name || null)
.filter(name => name !== null)
: null,
mpn,
url: url_path,
priceCurrency,
price,
itemCondition: 'https://schema.org/NewCondition',
availability: stock && is_in_stock ? 'InStock' : 'OutOfStock',
sku
}
}
}
5 changes: 5 additions & 0 deletions src/themes/default/components/core/blocks/Reviews/Reviews.vue
Expand Up @@ -10,6 +10,7 @@
:per-page="4"
:items="reviews"
:product-name="productName"
:product="product"
/>
</div>
<div class="col-xs-12 col-md-5 pt50">
Expand Down Expand Up @@ -136,6 +137,10 @@ export default {
productName: {
type: String,
default: ''
},
product: {
type: Object,
required: true
}
},
computed: {
Expand Down
17 changes: 12 additions & 5 deletions src/themes/default/components/theme/blocks/Reviews/ReviewsList.vue
Expand Up @@ -3,15 +3,14 @@
<div class="mt50 h5" v-if="!itemsPerPage || itemsPerPage.length === 0">
{{ $t('No reviews have been posted yet. Please don\'t hesitate to share Your opinion and write the first review!') }}
</div>
<div class="mt50" v-for="(item, index) in itemsPerPage" :key="index" itemprop="review" itemscope itemtype="http://schema.org/Review">
<meta itemprop="itemReviewed" :content="productName | htmlDecode">
<h4 class="weight-400 m0" itemprop="reviewAspect" :content="item.title">
<div class="mt50" v-for="(item, index) in itemsPerPage" :key="index">
<h4 class="weight-400 m0">
{{ item.title }}
</h4>
<p class="cl-tertiary mt10 mb20 fs-medium-small">
{{ item.nickname }}, {{ item.created_at | date(null, storeView) }}
</p>
<p class="cl-gray lh25" itemprop="reviewBody" :content="item.detail">
<p class="cl-gray lh25">
{{ item.detail }}
</p>
</div>
Expand All @@ -31,12 +30,13 @@
<i class="material-icons">chevron_right</i>
</a>
</div>
<script v-html="getJsonLd" type="application/ld+json" />
</div>
</template>

<script>
import { currentStoreView } from '@vue-storefront/core/lib/multistore'

import {reviewJsonLd} from '@vue-storefront/core/helpers'
export default {
props: {
perPage: {
Expand All @@ -51,6 +51,10 @@ export default {
productName: {
type: String,
default: ''
},
product: {
type: Object,
required: true
}
},
data () {
Expand Down Expand Up @@ -86,6 +90,9 @@ export default {
},
storeView () {
return currentStoreView()
},
getJsonLd () {
return reviewJsonLd(this.itemsPerPage, this.product, this.$store.state.storeView.i18n.currencyCode)
}
},
methods: {
Expand Down
24 changes: 9 additions & 15 deletions src/themes/default/pages/Product.vue
@@ -1,5 +1,5 @@
<template>
<div id="product" itemscope itemtype="http://schema.org/Product">
<div id="product">
<section class="bg-cl-secondary px20 product-top-section">
<div class="container">
<section class="row m0 between-xs">
Expand All @@ -18,7 +18,6 @@
<h1
class="mb20 mt0 cl-mine-shaft product-name"
data-testid="productName"
itemprop="name"
>
{{ getCurrentProduct.name | htmlDecode }}
<web-share
Expand All @@ -29,16 +28,11 @@
</h1>
<div
class="mb20 uppercase cl-secondary"
itemprop="sku"
:content="getCurrentProduct.sku"
>
{{ $t('SKU: {sku}', { sku: getCurrentProduct.sku }) }}
</div>
<div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
<meta itemprop="priceCurrency" :content="$store.state.storeView.i18n.currencyCode">
<meta itemprop="price" :content="parseFloat(getCurrentProduct.price_incl_tax).toFixed(2)">
<meta itemprop="availability" :content="structuredData.availability">
<meta itemprop="url" :content="getCurrentProduct.url_path">
<div>
<product-price
class="mb40"
v-if="getCurrentProduct.type_id !== 'grouped'"
Expand Down Expand Up @@ -150,7 +144,7 @@
<div class="h4 details-wrapper" :class="{'details-wrapper--open': detailsOpen}">
<div class="row between-md m0">
<div class="col-xs-12 col-sm-6">
<div class="lh30 h5" itemprop="description" v-html="getCurrentProduct.description" />
<div class="lh30 h5" v-html="getCurrentProduct.description" />
</div>
<div class="col-xs-12 col-sm-5">
<ul class="attributes p0 pt5 m0">
Expand All @@ -172,6 +166,7 @@
:product-name="getOriginalProduct.name"
:product-id="getOriginalProduct.id"
v-show="isOnline"
:product="getCurrentProduct"
/>
</lazy-hydrate>
<lazy-hydrate when-idle>
Expand All @@ -184,6 +179,7 @@
<related-products type="related" />
</lazy-hydrate>
<SizeGuide />
<script v-html="getJsonLd" type="application/ld+json" />
</div>
</template>

Expand Down Expand Up @@ -222,7 +218,7 @@ import { htmlDecode } from '@vue-storefront/core/filters'
import { ReviewModule } from '@vue-storefront/core/modules/review'
import { RecentlyViewedModule } from '@vue-storefront/core/modules/recently-viewed'
import { registerModule, isModuleRegistered } from '@vue-storefront/core/lib/modules'
import { onlineHelper, isServer } from '@vue-storefront/core/helpers'
import { onlineHelper, isServer, productJsonLd } from '@vue-storefront/core/helpers'
import { catalogHooksExecutors } from '@vue-storefront/core/modules/catalog-next/hooks'
import ProductPrice from 'theme/components/core/ProductPrice.vue'

Expand Down Expand Up @@ -284,11 +280,6 @@ export default {
isOnline (value) {
return onlineHelper.isOnline
},
structuredData () {
return {
availability: this.getCurrentProduct.stock && this.getCurrentProduct.stock.is_in_stock ? 'InStock' : 'OutOfStock'
}
},
getProductOptions () {
if (
this.getCurrentProduct.errors &&
Expand Down Expand Up @@ -329,6 +320,9 @@ export default {
},
storeView () {
return currentStoreView()
},
getJsonLd () {
return productJsonLd(this.getCurrentProduct, this.getCurrentProductConfiguration.color.label, this.$store.state.storeView.i18n.currencyCode, this.getCustomAttributes)
}
},
async mounted () {
Expand Down