-
Notifications
You must be signed in to change notification settings - Fork 145
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Shopper Experience]
ImageWithText
component (#991)
* init `ImageWithText` component * Add more ImageWithText props * Remove HTML paragraph tag from text * Sanitize HTML * Add `dompurify` to template-retail-react-app * Replace `dompurify` with `sanitize-html` ready for ssr * Revert "Replace `dompurify` with `sanitize-html` ready for ssr" This reverts commit 231183a. * bump bundle size * Add styles and basic test * Adjust text styles * Update packages/template-retail-react-app/app/components/experience/image-with-text/index.jsx Co-authored-by: Ben Chypak <[email protected]> * PR Feedback * Use Chakra UI components over native HTML elements * Update CHANGELOG.md * Remove DOMPurify * Remove example page --------- Co-authored-by: Ben Chypak <[email protected]>
- Loading branch information
Showing
4 changed files
with
173 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
129 changes: 129 additions & 0 deletions
129
packages/template-retail-react-app/app/components/experience/image-with-text/index.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* | ||
* Copyright (c) 2023, Salesforce, Inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
import React from 'react' | ||
import PropTypes from 'prop-types' | ||
import {Box, Image, Link, Text} from '@chakra-ui/react' | ||
|
||
/** | ||
* Image with text component | ||
* | ||
* @param {object} props | ||
* @param {string} - props.ITCLink - Image Link. | ||
* @param {string} - props.ITCText - Text Below Image. | ||
* @param {image} - props.image - Image. | ||
* @param {string} - props.heading - Text Overlay. | ||
* @param {string} - props.alt - The image alt text shown by the component. | ||
* @returns {React.ReactElement} - ImageWithText component. | ||
*/ | ||
const ImageWithText = ({ITCLink, ITCText, image, heading, alt}) => { | ||
const hasCaption = ITCText || heading | ||
|
||
return ( | ||
<Box className={'image-with-text'}> | ||
<Box | ||
as="figure" | ||
className={'image-with-text-figure'} | ||
position={'relative'} | ||
margin={0} | ||
width={'100%'} | ||
> | ||
<picture> | ||
<source srcSet={image?.src?.tablet} media="(min-width: 48em)" /> | ||
<source srcSet={image?.src?.desktop} media="(min-width: 64em)" /> | ||
<Link to={ITCLink}> | ||
<Image | ||
className={'image-with-text-image'} | ||
data-testid={'image-with-text-image'} | ||
src={image?.src?.mobile ? image?.src?.mobile : image?.url} | ||
ignoreFallback={true} | ||
alt={alt} | ||
title={alt} | ||
filter={'brightness(40%)'} | ||
/> | ||
</Link> | ||
</picture> | ||
{hasCaption && ( | ||
<Text as="figcaption"> | ||
{heading && ( | ||
<Box | ||
className={'image-with-text-heading-container'} | ||
position={'absolute'} | ||
top={'50%'} | ||
width={'100%'} | ||
> | ||
<Text | ||
as="span" | ||
className={'image-with-text-heading-text'} | ||
color={'white'} | ||
> | ||
{/* The `dangerouslySetInnerHTML` is safe to use in this context. */} | ||
{/* The HTML in the response from Page Designer API is already sanitized. */} | ||
<Box | ||
dangerouslySetInnerHTML={{ | ||
__html: heading | ||
}} | ||
sx={{ | ||
p: { | ||
display: 'flex', | ||
alignItems: 'center', | ||
justifyContent: 'center' | ||
} | ||
}} | ||
/> | ||
</Text> | ||
</Box> | ||
)} | ||
{ITCText && ( | ||
<Box> | ||
<Text as="span" className={'image-with-text-text-underneath'}> | ||
{/* The `dangerouslySetInnerHTML` is safe to use in this context. */} | ||
{/* The HTML in the response from Page Designer API is already sanitized. */} | ||
<Box | ||
dangerouslySetInnerHTML={{ | ||
__html: ITCText | ||
}} | ||
sx={{ | ||
p: { | ||
display: 'flex', | ||
alignItems: 'center', | ||
justifyContent: 'center' | ||
} | ||
}} | ||
/> | ||
</Text> | ||
</Box> | ||
)} | ||
</Text> | ||
)} | ||
</Box> | ||
</Box> | ||
) | ||
} | ||
|
||
ImageWithText.propTypes = { | ||
ITCLink: PropTypes.string, | ||
ITCText: PropTypes.string, | ||
image: PropTypes.shape({ | ||
_type: PropTypes.string, | ||
focalPoint: PropTypes.shape({ | ||
_type: PropTypes.string, | ||
x: PropTypes.number, | ||
y: PropTypes.number | ||
}), | ||
metaData: PropTypes.shape({ | ||
_type: PropTypes.string, | ||
height: PropTypes.number, | ||
width: PropTypes.number | ||
}), | ||
url: PropTypes.string, | ||
src: PropTypes.string || PropTypes.object | ||
}).isRequired, | ||
heading: PropTypes.string, | ||
alt: PropTypes.string | ||
} | ||
|
||
export default ImageWithText |
38 changes: 38 additions & 0 deletions
38
packages/template-retail-react-app/app/components/experience/image-with-text/index.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright (c) 2023, Salesforce, Inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
import React from 'react' | ||
import ImageWithText from './index' | ||
import {renderWithProviders} from '../../../utils/test-utils' | ||
|
||
const SAMPLE_DATA = { | ||
ITCLink: 'https://salesforce.com', | ||
ITCText: | ||
'<p><em>Text</em> <strong>Below</strong> <u>Image</u> <s>test</s> Image With Text Component <a href="https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArchGlobal/homepage-example.html?lang=en_US" target="_self" data-link-type="page" data-link-label="homepage-example" data-content-page-id="homepage-example">Home-page example link</a><img src="https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Library-Sites-RefArchSharedLibrary/default/dw89c1031d/images/myaccount_addresses.png" alt="alt tag myaccount_addresses image"></p>', | ||
image: { | ||
_type: 'Image', | ||
focalPoint: { | ||
_type: 'Imagefocalpoint', | ||
x: 0.5, | ||
y: 0.5 | ||
}, | ||
metaData: { | ||
_type: 'Imagemetadata', | ||
height: 1280, | ||
width: 1920 | ||
}, | ||
url: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Library-Sites-RefArchSharedLibrary/default/dw34c389b5/images/SearchBanner/search.jpg' | ||
}, | ||
heading: | ||
'<p><em>Text</em> <strong>Overlay</strong> <s>test</s> <u>Image</u> With Text <a href="https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArchGlobal/mens/?lang=en_US" target="_self" data-link-type="category" data-link-label="Mens" data-category-id="mens" data-category-catalog-id="storefront-catalog-m-en">Component Link</a><img src="https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Library-Sites-RefArchSharedLibrary/default/dw2d9142bf/images/myaccount_registry.png" alt="alt text myaccount_registry image"></p>', | ||
alt: 'Alt Text test Image With Text Component' | ||
} | ||
|
||
test('Page renders correct component', () => { | ||
const {getByText} = renderWithProviders(<ImageWithText {...SAMPLE_DATA} />) | ||
|
||
expect(getByText(/image with text component/i)).toBeInTheDocument() | ||
}) |