diff --git a/packages/extensions/venia-sample-language-packs/i18n/fr_FR.json b/packages/extensions/venia-sample-language-packs/i18n/fr_FR.json index db5c2a019f..5f6380ab25 100644 --- a/packages/extensions/venia-sample-language-packs/i18n/fr_FR.json +++ b/packages/extensions/venia-sample-language-packs/i18n/fr_FR.json @@ -134,7 +134,6 @@ "createWishlist.dialogTitle": "Nouvelle liste de favoris", "createWishlist.handleCreateListText": "Créer une liste", "createWishlist.listName": "Nom de la liste", - "customAttributes.title": "Détails", "Customer Service": "Service à la clientèle", "customerForm.defaultShipping": "Faites-en mon adresse par défaut", "customerForm.formMessage": "L'adresse de livraison que vous entrez sera enregistrée dans votre carnet d'adresses et définie par défaut pour vos futurs achats.", @@ -312,6 +311,7 @@ "productFullDetail.errorToken": "Un problème est survenu avec votre panier. Veuillez vous reconnecter et réessayer d'ajouter l'élément.", "productFullDetail.errorUnknown": "Impossible d'ajouter l'article au panier. Veuillez vérifier les options requises et réessayer.", "productFullDetail.productDescription": "Description du produit", + "productFullDetail.details": "Détails", "productList.each": " ch.", "productList.outOfStock": "en rupture de stock", "productList.quantity": "Qté : {quantity}", diff --git a/packages/pagebuilder/lib/ContentTypes/Banner/banner.js b/packages/pagebuilder/lib/ContentTypes/Banner/banner.js index 4f9b3b0c05..e38907e106 100644 --- a/packages/pagebuilder/lib/ContentTypes/Banner/banner.js +++ b/packages/pagebuilder/lib/ContentTypes/Banner/banner.js @@ -3,7 +3,7 @@ import defaultClasses from './banner.module.css'; import { useStyle } from '@magento/venia-ui/lib/classify'; import { arrayOf, bool, oneOf, shape, string, func, object } from 'prop-types'; import Button from '@magento/venia-ui/lib/components/Button/button'; -import resolveLinkProps from '../../resolveLinkProps'; +import resolveLinkProps from '@magento/peregrine/lib/util/resolveLinkProps'; import { Link, useHistory } from 'react-router-dom'; import resourceUrl from '@magento/peregrine/lib/util/makeUrl'; import useIntersectionObserver from '@magento/peregrine/lib/hooks/useIntersectionObserver'; diff --git a/packages/pagebuilder/lib/ContentTypes/ButtonItem/buttonItem.js b/packages/pagebuilder/lib/ContentTypes/ButtonItem/buttonItem.js index 17efb7ac31..b2facf581d 100644 --- a/packages/pagebuilder/lib/ContentTypes/ButtonItem/buttonItem.js +++ b/packages/pagebuilder/lib/ContentTypes/ButtonItem/buttonItem.js @@ -4,7 +4,7 @@ import { useHistory } from 'react-router-dom'; import { useStyle } from '@magento/venia-ui/lib/classify'; import Button from '@magento/venia-ui/lib/components/Button/button'; -import resolveLinkProps from '../../resolveLinkProps'; +import resolveLinkProps from '@magento/peregrine/lib/util/resolveLinkProps'; import defaultClasses from './buttonItem.module.css'; /** diff --git a/packages/pagebuilder/lib/ContentTypes/Image/image.js b/packages/pagebuilder/lib/ContentTypes/Image/image.js index 1d775b4e31..7b7b2b74d0 100644 --- a/packages/pagebuilder/lib/ContentTypes/Image/image.js +++ b/packages/pagebuilder/lib/ContentTypes/Image/image.js @@ -2,7 +2,7 @@ import React from 'react'; import defaultClasses from './image.module.css'; import { arrayOf, bool, oneOf, shape, string, number } from 'prop-types'; import { Link } from 'react-router-dom'; -import resolveLinkProps from '../../resolveLinkProps'; +import resolveLinkProps from '@magento/peregrine/lib/util/resolveLinkProps'; import { useStyle } from '@magento/venia-ui/lib/classify'; import resourceUrl from '@magento/peregrine/lib/util/makeUrl'; diff --git a/packages/pagebuilder/lib/ContentTypes/Text/text.js b/packages/pagebuilder/lib/ContentTypes/Text/text.js index 056b31fb17..9841a54325 100644 --- a/packages/pagebuilder/lib/ContentTypes/Text/text.js +++ b/packages/pagebuilder/lib/ContentTypes/Text/text.js @@ -5,7 +5,9 @@ import defaultClasses from './text.module.css'; import { useHistory } from 'react-router-dom'; import handleHtmlContentClick from '../../handleHtmlContentClick'; -const toHTML = str => ({ __html: str }); +import htmlStringImgUrlConverter from '@magento/peregrine/lib/util/htmlStringImgUrlConverter'; + +const toHTML = str => ({ __html: htmlStringImgUrlConverter(str) }); /** * Page Builder Text component. diff --git a/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js b/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js index 325554a4f1..302b91fa51 100644 --- a/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js +++ b/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js @@ -460,6 +460,7 @@ export const useProductFullDetail = props => { // Normalization object for product details we need for rendering. const productDetails = { description: product.description, + shortDescription: product.short_description, name: product.name, price: productPrice, sku: product.sku diff --git a/packages/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js b/packages/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js index 764c883001..cbd4df37e0 100644 --- a/packages/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js +++ b/packages/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js @@ -13,6 +13,9 @@ export const ProductDetailsFragment = gql` description { html } + short_description { + html + } id uid # eslint-disable-next-line @graphql-eslint/require-id-when-available diff --git a/packages/peregrine/lib/util/__tests__/resolveLinkProps.spec.js b/packages/peregrine/lib/util/__tests__/resolveLinkProps.spec.js new file mode 100644 index 0000000000..4f444774a2 --- /dev/null +++ b/packages/peregrine/lib/util/__tests__/resolveLinkProps.spec.js @@ -0,0 +1,53 @@ +import resolveLinkProps from '../resolveLinkProps'; + +describe('resolve to internal link', () => { + process.env.MAGENTO_BACKEND_URL = 'http://magento.com/'; + + test('when base url matches', () => { + const linkProps = resolveLinkProps('http://magento.com/cms-page'); + expect(linkProps).toEqual({ + to: '/cms-page' + }); + }); + + test('when base url matches product URL', () => { + const linkProps = resolveLinkProps( + 'http://magento.com/product-page.html' + ); + expect(linkProps).toEqual({ + to: '/product-page.html' + }); + }); + + test('with root-relative url', () => { + const linkProps = resolveLinkProps('/cms-page'); + expect(linkProps).toEqual({ + to: '/cms-page' + }); + }); + + test('with relative url', () => { + const linkProps = resolveLinkProps('cms-page'); + expect(linkProps).toEqual({ + to: '/cms-page' + }); + }); +}); + +test('resolve to external anchor if external link', () => { + process.env.MAGENTO_BACKEND_URL = 'http://magento.com/'; + const linkProps = resolveLinkProps( + 'http://not-magento.com/product-page.html' + ); + expect(linkProps).toEqual({ + href: 'http://not-magento.com/product-page.html' + }); +}); + +test('return original input if input is invalid', () => { + process.env.MAGENTO_BACKEND_URL = null; + const linkProps = resolveLinkProps(null); + expect(linkProps).toEqual({ + href: null + }); +}); diff --git a/packages/peregrine/lib/util/htmlStringImgUrlConverter.js b/packages/peregrine/lib/util/htmlStringImgUrlConverter.js new file mode 100644 index 0000000000..96779fb97c --- /dev/null +++ b/packages/peregrine/lib/util/htmlStringImgUrlConverter.js @@ -0,0 +1,26 @@ +import makeUrl from './makeUrl'; +import resolveLinkProps from './resolveLinkProps'; + +/** + * Modifies html string images to use makeUrl as source and resolves links to use internal path. + * + * @param {string} htmlString - the html string to be updated + * @return {string} + */ +const htmlStringImgUrlConverter = htmlString => { + const temporaryElement = document.createElement('div'); + temporaryElement.innerHTML = htmlString; + for (const imgElement of temporaryElement.getElementsByTagName('img')) { + imgElement.src = makeUrl(imgElement.src, { + type: 'image-wysiwyg', + quality: 85 + }); + } + for (const linkElement of temporaryElement.getElementsByTagName('a')) { + const linkProps = resolveLinkProps(linkElement.href); + linkElement.href = linkProps.to || linkProps.href; + } + return temporaryElement.innerHTML; +}; + +export default htmlStringImgUrlConverter; diff --git a/packages/peregrine/lib/util/resolveLinkProps.js b/packages/peregrine/lib/util/resolveLinkProps.js new file mode 100644 index 0000000000..f8262f1b42 --- /dev/null +++ b/packages/peregrine/lib/util/resolveLinkProps.js @@ -0,0 +1,25 @@ +/** + * Resolve link properties + * + * @param {string} link + */ +export default link => { + let isExternalUrl; + const linkProps = {}; + + try { + const baseUrlObj = new URL(process.env.MAGENTO_BACKEND_URL); + const urlObj = new URL(link, baseUrlObj); + isExternalUrl = baseUrlObj.host !== urlObj.host; + + if (isExternalUrl) { + linkProps['href'] = link; + } else { + linkProps['to'] = urlObj.pathname; + } + } catch (e) { + linkProps['href'] = link; + } + + return linkProps; +}; diff --git a/packages/venia-ui/i18n/en_US.json b/packages/venia-ui/i18n/en_US.json index 330e88de57..ec3d0f7c6e 100644 --- a/packages/venia-ui/i18n/en_US.json +++ b/packages/venia-ui/i18n/en_US.json @@ -155,7 +155,6 @@ "createWishlistForm.privateRadio": "Private", "createWishlistForm.publicRadio": "Public", "createWishlistForm.saveButton": "Save", - "customAttributes.title": "Details", "Customer Service": "Customer Service", "customerForm.defaultShipping": "Make this my default address", "customerForm.formMessage": "The shipping address you enter will be saved to your address book and set as your default for future purchases.", @@ -357,6 +356,7 @@ "productFullDetail.errorToken": "There was a problem with your cart. Please sign in again and try adding the item once more.", "productFullDetail.errorUnknown": "Could not add item to cart. Please check required options and try again.", "productFullDetail.productDescription": "Product Description", + "productFullDetail.details": "Details", "productFullDetail.unavailableProduct": "This product is currently unavailable for purchase.", "productImageCarousel.previousButtonAriaLabel": "Previous Image", "productImageCarousel.nextButtonAriaLabel": "Next Image", diff --git a/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Select/select.module.css b/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Select/select.module.css index 14e3f64e1e..dc79aa720f 100644 --- a/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Select/select.module.css +++ b/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Select/select.module.css @@ -9,5 +9,5 @@ } .contentHtml { - composes: block from global; + composes: inline-block from global; } diff --git a/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Text/text.module.css b/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Text/text.module.css index 14e3f64e1e..dc79aa720f 100644 --- a/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Text/text.module.css +++ b/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Text/text.module.css @@ -9,5 +9,5 @@ } .contentHtml { - composes: block from global; + composes: inline-block from global; } diff --git a/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Textarea/textarea.js b/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Textarea/textarea.js index 06df5feed1..3ebdb69972 100644 --- a/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Textarea/textarea.js +++ b/packages/venia-ui/lib/components/ProductFullDetail/CustomAttributes/AttributeType/Textarea/textarea.js @@ -18,11 +18,16 @@ import defaultClasses from './textarea.module.css'; */ const Textarea = props => { const classes = useStyle(defaultClasses, props.classes); - const { attribute_metadata = {}, entered_attribute_value = {} } = props; + const { + attribute_metadata = {}, + entered_attribute_value = {}, + showLabels = true + } = props; - const attributeLabel = attribute_metadata.label ? ( -
- Details -
-