We continue the series of articles with technical lifehacks, dedicated to PWA Studio. After the overview of how to Insert CMS Block on the Product Page, we suggest to check out some more interesting situations.
Imagine a store using the PWA Studio storefront where the Product Page should contain the product’s short description. You may see nothing unusual here, but there is a trick – the default Venia theme doesn’t show the product’s short description by default. Oops. But no worries, this is not difficult to fix. Thanks to PWA Studio’s extensibility framework.
In order to solve our task, we need to perform two major steps:
- Create a custom component that will show the product’s short description.
- Insert that component into Venia’s product page component.
Insert the Custom Component into the ProductFullDetail
The product’s short description will be inserted to the page after the product’s title. In the ProductFullDetail
component, the title is wrapped by the section <section className={classes.title}>
, which will be taken as a reference to insert the short description.
To make things work, we’ll add the adjustments into the project’s local-intercept.js
file. Read what this file is about and how it works in the “Insert CMS Block on the Product Page right before footer” article.
// file: local-intercept.js
function localIntercept(targets) {
const {Targetables} = require('@magento/pwa-buildpack');
const targetables = Targetables.using(targets);
// load the component to be customized
const ProductFullDetailComponent = targetables.reactComponent(
'@magento/venia-ui/lib/components/ProductFullDetail/productFullDetail.js'
);
// import the custom component in the component to be modified
const ProductShortDescription = ProductFullDetailComponent.addImport(
"ProductShortDescription from '@theme/components/ProductShortDescription'"
);
// insert the custom component that renders product's short description
ProductFullDetailComponent.insertAfterJSX(
'<section className={classes.title}>',
`<${ProductShortDescription} url_key={product.url_key} />`
);
}
module.exports = localIntercept;
It is important to highlight that our custom component receives the url_key
as a property from the Venia’s ProductFullDetail
component. It is not required from our side to do something to pass it [the property], because our custom component has been inserted into the ProductFullDetail
component. This allows all required data (url_key
) for our custom component to be present in product.url_key
.
Also, you might notice that the ProductShortDescription
component is imported from some @theme
. That’s our project’s src
directory. It is configured using webpack alias in the project’s webpack.config.js
file. Below you can find the detailed instruction how to make it work.
Add Project’s src
dir Alias in Webpack
To assure easier import of the components, we recommend adding the alias. That’s not obligatory, but a good thing to have. With these adjustments in place, we will avoid imports like:
import ProductShortDescription from '../../../../../../components/ProductShortDescription';
In order to add the alias, we shall incorporate following changes into webpack.config.js
:
// file: webpack.config.js
// [...]
const path = require('path');
module.exports = async env => {
// [...]
config.resolve = {
...config.resolve,
alias: {
...config.resolve.alias,
'@theme': path.resolve(__dirname, 'src')
}
}
// [...]
return config;
};
Custom Component
Now, we will work on the component that renders the data – Short Description.
It requires the product url_key
to know for what exact product the short description will be shown. It uses a GraphQL query GET_PRODUCT_SHORT_DESCRIPTION
that is passed down to the talon useProductShortDescription
, which is responsible for fetching and providing the product’s short description.
// file: src/components/ProductShortDescription/productShortDescription.js
import React, { Fragment } from 'react';
import { useProductShortDescription } from '@theme/talons/ProductShortDescription/useProductShortDescription';
import { GET_PRODUCT_SHORT_DESCRIPTION } from './productShortDescription.gql';
import { fullPageLoadingIndicator } from '@magento/venia-ui/lib/components/LoadingIndicator';
import RichText from '@magento/venia-ui/lib/components/RichText';
import { string } from 'prop-types';
const ProductShortDescription = props => {
const { url_key } = props;
const talonProps = useProductShortDescription({
url_key,
getProductShortDescription: GET_PRODUCT_SHORT_DESCRIPTION
});
const {
error,
loading,
shortDescription
} = talonProps;
if (error && !shortDescription) {
return null;
}
if (loading) {
return fullPageLoadingIndicator
}
return (
<Fragment>
<RichText content={shortDescription} />
</Fragment>
);
}
ProductShortDescription.propTypes = {
url_key: string.isRequired
}
export default ProductShortDescription;
// file: file: src/components/ProductShortDescription/index.js
import ProductShortDescription from "./productShortDescription.js";
export default ProductShortDescription;
GraphQL Query to Fetch Product’s Short Description
// file: src/components/ProductShortDescription/productShortDescription.gql.js
import { gql } from '@apollo/client';
export const GET_PRODUCT_SHORT_DESCRIPTION = gql`
query getProductShortDescription($urlKey: String!) {
products(filter: { url_key: { eq: $urlKey } }) {
items {
id
url_key,
short_description {
html
}
}
}
}
`;
Talon that Fetches the Product’s Short Description – useProductShortDescription
// file: src/talons/ProdutShortDescription/useProductShortDescription.js
import React, { useMemo } from 'react';
import { useQuery } from '@apollo/client';
import { string, func } from 'prop-types';
export const useProductShortDescription = props => {
const {
url_key,
getProductShortDescription
} = props;
const { error, loading, data } = useQuery(getProductShortDescription, {
fetchPolicy: 'cache-and-network',
nextFetchPolicy: 'cache-first',
variables: {
urlKey: url_key
}
});
const shortDescription = useMemo(() => {
if (!data || !data.products || !data.products.items) {
return null;
}
const product = data.products.items.find(
item => item.url_key === url_key
);
if (!product) {
return null;
}
return product.short_description.html;
}, [data, url_key]);
return {
data,
shortDescription,
error,
loading
}
}
useProductShortDescription.propTypes = {
url_key: string.isRequired,
getProductShortDescription: func.isRequired
}
That’s it! These are all the required changes to insert product’s short description in PWA Studio under its title on the product details page.
Using the same approach, it is possible to add any other information to the product page, like displaying some custom product attributes, for example. Of course, some adjustments are needed, but that’s not a big deal when almost everything is prepared.
Read also: Magento PWA Plugins
Good luck! And do not hesitate to let your friends and teammates know what you’ve just learned ;)