diff --git a/src/components/src/AppHeader/appheader.main.tsx b/src/components/src/AppHeader/appheader.main.tsx index 142bdbd2c..1eb208c4d 100644 --- a/src/components/src/AppHeader/appheader.main.tsx +++ b/src/components/src/AppHeader/appheader.main.tsx @@ -39,6 +39,7 @@ import { login } from '../utils/AuthService'; import './appheader.main.less'; import { getConfig, IEpConfig } from '../utils/ConfigProvider'; +import ImageContainer from '../ImageContainer/image.container'; let Config: IEpConfig | any = {}; let intl = { get: str => str }; @@ -283,15 +284,7 @@ class AppHeaderMain extends Component {
- Header logo { - const element: any = e.target; - element.src = headerLogo; - }} - /> +
diff --git a/src/components/src/B2cHomePage/b2c.home.page.tsx b/src/components/src/B2cHomePage/b2c.home.page.tsx index 7bc359761..e0f6bbbb7 100644 --- a/src/components/src/B2cHomePage/b2c.home.page.tsx +++ b/src/components/src/B2cHomePage/b2c.home.page.tsx @@ -25,6 +25,7 @@ import { getConfig, IEpConfig } from '../utils/ConfigProvider'; import Carousel from '../Carousel/carousel.homepage'; import IndiRecommendationsDisplayMain from '../IndiRecommendations/indirecommendations.main'; +import ImageContainer from '../ImageContainer/image.container'; import './b2c.home.page.less'; @@ -61,7 +62,7 @@ const B2CHomePage: React.FunctionComponent = () => { {/* eslint-disable-next-line no-return-assign */}
- home-espot-2 { e.target.src = homeEspot2; }} /> +
{intl.get('home-sub-espot-container2-first-line')} @@ -75,7 +76,7 @@ const B2CHomePage: React.FunctionComponent = () => {
- home-espot-1 { e.target.src = homeEspotParallax1; }} /> +
@@ -91,7 +92,7 @@ const B2CHomePage: React.FunctionComponent = () => {
- home-espot-3 { e.target.src = homeEspot3; }} /> +
{intl.get('home-sub-espot-container3-first-line')} @@ -100,12 +101,12 @@ const B2CHomePage: React.FunctionComponent = () => { {intl.get('home-sub-espot-container3-second-line')}
- home-espot-3 { e.target.src = homeEspot3; }} /> +
- home-espot-4 { e.target.src = homeEspot4; }} /> +
{intl.get('home-sub-espot-container4-first-line')} @@ -119,7 +120,7 @@ const B2CHomePage: React.FunctionComponent = () => {
- home-espot-1 { e.target.src = homeEspotParallax2; }} /> +
diff --git a/src/components/src/Carousel/carousel.homepage.tsx b/src/components/src/Carousel/carousel.homepage.tsx index 7ee78b8ac..e8e91eecb 100644 --- a/src/components/src/Carousel/carousel.homepage.tsx +++ b/src/components/src/Carousel/carousel.homepage.tsx @@ -22,6 +22,7 @@ import React from 'react'; import Slider from 'react-slick'; import { getConfig, IEpConfig } from '../utils/ConfigProvider'; +import ImageContainer from '../ImageContainer/image.container'; import carouselBaner1 from '../../../images/carousel-images/baner_1.jpg'; import carouselBaner2 from '../../../images/carousel-images/baner_2.jpg'; @@ -51,7 +52,7 @@ const Carousel: React.FunctionComponent = () => { customPaging(i) { return (
- img { const element: any = e.target; element.src = carouselBanerArray[i]; }} /> +
); }, @@ -78,44 +79,16 @@ const Carousel: React.FunctionComponent = () => {
- img { - const element: any = e.target; - element.src = carouselBaner1; - }} - /> +
- img { - const element: any = e.target; - element.src = carouselBaner2; - }} - /> +
- img { - const element: any = e.target; - element.src = carouselBaner3; - }} - /> +
- img { - const element: any = e.target; - element.src = carouselBaner4; - }} - /> +
diff --git a/src/components/src/CartLineItem/cart.lineitem.tsx b/src/components/src/CartLineItem/cart.lineitem.tsx index ffe30de36..a4455bd3a 100644 --- a/src/components/src/CartLineItem/cart.lineitem.tsx +++ b/src/components/src/CartLineItem/cart.lineitem.tsx @@ -29,9 +29,9 @@ import AppModalBundleConfigurationMain from '../AppModalBundleConfiguration/appm import DropdownCartSelection from '../DropdownCartSelection/dropdown.cart.selection.main'; import './cart.lineitem.less'; -import imgPlaceholder from '../../../images/img_missing_horizontal@2x.png'; import { ReactComponent as UpdateQuantityIcon } from '../../../images/icons/ic_update.svg'; import { ReactComponent as RecycleBinIcon } from '../../../images/icons/ic_trash.svg'; +import ImageContainer from '../ImageContainer/image.container'; let Config: IEpConfig | any = {}; let intl = { get: str => str }; @@ -510,7 +510,7 @@ class CartLineItem extends Component { : ('') } - { const element: any = e.target; element.src = imgPlaceholder; }} alt="Not Available" className="cart-lineitem-thumbnail" /> +
diff --git a/src/components/src/ImageContainer/README.md b/src/components/src/ImageContainer/README.md new file mode 100644 index 000000000..6f68f5764 --- /dev/null +++ b/src/components/src/ImageContainer/README.md @@ -0,0 +1,23 @@ +# ImageContainer + +#### Description + +Initially trying to fetch primary file type received from the prop if not found try PNG, if not found try JPG, if not found try SVG etc. + +The configuration of these file types may be managed within `ep.config.json`. If no file types are provided in the configuration, the image will be used as is provided in the URL. + +#### Usage + +```js +import ImageContainer from '../ImageContainer/image.container'; +``` + +#### Example + +```js + +``` + +#### Properties + + diff --git a/src/components/src/ImageContainer/image.container.stories.tsx b/src/components/src/ImageContainer/image.container.stories.tsx new file mode 100644 index 000000000..223f85f26 --- /dev/null +++ b/src/components/src/ImageContainer/image.container.stories.tsx @@ -0,0 +1,38 @@ +/** + * Copyright © 2019 Elastic Path Software Inc. All rights reserved. + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this license. If not, see + * + * https://www.gnu.org/licenses/ + * + * + */ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import Readme from './README.md'; +import ImageContainer from './image.container'; +import homeEspotParallax1 from '../../../images/site-images/parallax-banner-1.jpg'; + +storiesOf('Components|ImageContainer', module) + .addParameters({ + readme: { + // Show readme at the addons panel + sidebar: Readme, + }, + }) + .add('ImageContainer', () => { + const homeEspotParallax1FileName = 'parallax-banner-1.jpg'; + + return (); + }); diff --git a/src/components/src/ImageContainer/image.container.tsx b/src/components/src/ImageContainer/image.container.tsx new file mode 100644 index 000000000..18fd626af --- /dev/null +++ b/src/components/src/ImageContainer/image.container.tsx @@ -0,0 +1,90 @@ +/** + * Copyright © 2019 Elastic Path Software Inc. All rights reserved. + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this license. If not, see + * + * https://www.gnu.org/licenses/ + * + * + */ + +import React from 'react'; +import { getConfig, IEpConfig } from '../utils/ConfigProvider'; +import imgPlaceholder from '../../../images/img_missing_horizontal@2x.png'; + +let Config: IEpConfig | any = {}; + +interface ImageContainerProps { + /** name of file */ + fileName: string; + /** image URL */ + imgUrl: string; + /** class name */ + className?: string; + /** is a sku image */ + isSkuImage?: boolean; + /** loading data */ + onLoadData?: (...args: any[]) => any; +} + +function ImageContainer(props: ImageContainerProps) { + const epConfig = getConfig(); + Config = epConfig.config; + const { + fileName, imgUrl, className, isSkuImage, onLoadData, + } = props; + + const imageSource = isSkuImage ? Config.skuImagesUrl.replace('%sku%', fileName) : Config.siteImagesUrl.replace('%fileName%', fileName); + + const handleError = (e, defaultImgUrl) => { + const { src } = e.target; + if (Config.imageFileTypes && Config.imageFileTypes.enable && e.target['data-source'] !== 'default') { + const fallbackTypes = Config.imageFileTypes.types; + const initType = imgUrl.replace(/.*(?=\.)/g, ''); + const types = (fallbackTypes && fallbackTypes.length > 0) ? [ + initType, + ...fallbackTypes.filter(fileType => fileType !== initType), + ] : []; + const currentType = src.replace(/.*(?=\.)/g, ''); + const i = types.indexOf(currentType); + if (types[i + 1]) { + e.target.src = src.replace(currentType, types[i + 1]); + } else { + e.target.src = isSkuImage ? imgPlaceholder : defaultImgUrl; + e.target['data-source'] = 'default'; + } + } else if (e.target['data-source'] !== 'default') { + e.target.src = isSkuImage ? imgPlaceholder : defaultImgUrl; + e.target['data-source'] = 'default'; + } + }; + + return ( + img handleError(e, imgUrl)} + onLoad={onLoadData} + /> + ); +} + +ImageContainer.defaultProps = { + className: '', + isSkuImage: false, + onLoadData: () => {}, +}; + +export default ImageContainer; diff --git a/src/components/src/OrderTableLineItem/ordertable.lineitem.tsx b/src/components/src/OrderTableLineItem/ordertable.lineitem.tsx index 723f0f41c..9161f39b9 100644 --- a/src/components/src/OrderTableLineItem/ordertable.lineitem.tsx +++ b/src/components/src/OrderTableLineItem/ordertable.lineitem.tsx @@ -22,8 +22,8 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { getConfig, IEpConfig } from '../utils/ConfigProvider'; -import imgPlaceholder from '../../../images/img_missing_horizontal@2x.png'; import './ordertable.lineitem.less'; +import ImageContainer from '../ImageContainer/image.container'; let Config: IEpConfig | any = {}; @@ -106,15 +106,7 @@ function OrderTableLineItem(props: OrderTableLineItemProps) { return ( - Not Available { - const element:any = e.target; - element.src = imgPlaceholder; - }} - /> + diff --git a/src/components/src/ProductDisplayItem/productdisplayitem.main.tsx b/src/components/src/ProductDisplayItem/productdisplayitem.main.tsx index aa0e29b73..29e5d7827 100644 --- a/src/components/src/ProductDisplayItem/productdisplayitem.main.tsx +++ b/src/components/src/ProductDisplayItem/productdisplayitem.main.tsx @@ -24,7 +24,6 @@ import Slider from 'react-slick'; import { InlineShareButtons } from 'sharethis-reactjs'; import { login } from '../utils/AuthService'; import { itemLookup, cortexFetchItemLookupForm } from '../utils/CortexLookup'; -import imgMissingHorizontal from '../../../images/img_missing_horizontal@2x.png'; import ProductRecommendationsDisplayMain from '../ProductRecommendations/productrecommendations.main'; import IndiRecommendationsDisplayMain from '../IndiRecommendations/indirecommendations.main'; import BundleConstituentsDisplayMain from '../BundleConstituents/bundleconstituents.main'; @@ -36,6 +35,7 @@ import { useRequisitionListCountDispatch } from '../requisition-list-count-conte import './productdisplayitem.main.less'; import PowerReview from '../PowerReview/powerreview.main'; +import ImageContainer from '../ImageContainer/image.container'; // Array of zoom parameters to pass to Cortex const zoomArray = [ @@ -580,15 +580,7 @@ class ProductDisplayItemMain extends Component - { - const element: any = e.target; - element.src = imgMissingHorizontal; - }} - alt={intl.get('none-available')} - className="itemdetail-main-img" - /> + ); } @@ -596,7 +588,7 @@ class ProductDisplayItemMain extends Component
- { const element: any = e.target; element.src = imgMissingHorizontal; }} alt={intl.get('none-available')} className="itemdetail-main-img" /> +
diff --git a/src/components/src/ProductListItem/productlistitem.main.tsx b/src/components/src/ProductListItem/productlistitem.main.tsx index 6950132d7..b0b0ddb12 100644 --- a/src/components/src/ProductListItem/productlistitem.main.tsx +++ b/src/components/src/ProductListItem/productlistitem.main.tsx @@ -24,9 +24,9 @@ import { Link } from 'react-router-dom'; import { getConfig, IEpConfig } from '../utils/ConfigProvider'; import { login } from '../utils/AuthService'; import { itemLookup, cortexFetchItemLookupForm } from '../utils/CortexLookup'; -import imgPlaceholder from '../../../images/img_missing_horizontal@2x.png'; import './productlistitem.main.less'; +import ImageContainer from '../ImageContainer/image.container'; let Config: IEpConfig | any = {}; let intl = { get: str => str }; @@ -70,6 +70,7 @@ class ProductListItemMain extends Component
- { e.target.src = imgPlaceholder; }} alt="default" className="category-item-thumbnail img-responsive" onLoad={this.handleImageLoaded.bind(this)} title="" /> + { this.handleImageLoaded(); }} />
diff --git a/src/components/src/QuickOrderForm/quickorderform.tsx b/src/components/src/QuickOrderForm/quickorderform.tsx index 998162f41..d4e96ae57 100644 --- a/src/components/src/QuickOrderForm/quickorderform.tsx +++ b/src/components/src/QuickOrderForm/quickorderform.tsx @@ -23,10 +23,10 @@ import React, { Component } from 'react'; import './quickorderform.less'; -import imgPlaceholder from '../../../images/img_missing_horizontal@2x.png'; import { login } from '../utils/AuthService'; import { cortexFetchItemLookupForm, itemLookup } from '../utils/CortexLookup'; import { getConfig, IEpConfig } from '../utils/ConfigProvider'; +import ImageContainer from '../ImageContainer/image.container'; let Config: IEpConfig | any = {}; let intl = { get: (str, ...args: any[]) => str }; @@ -292,15 +292,7 @@ class QuickOrderForm extends Component {(code && product._definition) ? (
- { - const element: any = e.target; - element.src = imgPlaceholder; - }} - alt="Not Available" - className="cart-lineitem-thumbnail" - /> +

diff --git a/src/components/src/index.ts b/src/components/src/index.ts index 2e2063a68..8c212e6ca 100644 --- a/src/components/src/index.ts +++ b/src/components/src/index.ts @@ -94,6 +94,7 @@ import { CountProvider } from './cart-count-context'; import { RequisitionListCountProvider } from './requisition-list-count-context'; import B2BHomePage from './B2bHomePage/b2b.home.page'; import B2CHomePage from './B2cHomePage/b2c.home.page'; +import ImageContainer from './ImageContainer/image.container'; import DropdownCartSelection from './DropdownCartSelection/dropdown.cart.selection.main'; export { @@ -172,5 +173,6 @@ export { RequisitionListCountProvider, B2BHomePage, B2CHomePage, + ImageContainer, DropdownCartSelection, }; diff --git a/src/containers/CheckoutPage.tsx b/src/containers/CheckoutPage.tsx index 0d2d26bab..fe1b3e216 100644 --- a/src/containers/CheckoutPage.tsx +++ b/src/containers/CheckoutPage.tsx @@ -511,7 +511,7 @@ class CheckoutPage extends React.Component

{this.renderBillingAddress()}
-
diff --git a/src/containers/MaintenancePage.tsx b/src/containers/MaintenancePage.tsx index e09202f00..7e2e2f994 100644 --- a/src/containers/MaintenancePage.tsx +++ b/src/containers/MaintenancePage.tsx @@ -23,9 +23,9 @@ import React from 'react'; import intl from 'react-intl-universal'; import { Link } from 'react-router-dom'; import './MaintenancePage.less'; -import Config from '../ep.config.json'; import doge from '../images/site-images/maintenance-banner.png'; +import ImageContainer from '../components/src/ImageContainer/image.container'; const dogeFileName = 'maintenance-banner.png'; @@ -56,7 +56,7 @@ function MaintenancePage() {
- dog-espot-1 { e.target.src = doge; }} /> +

{intl.get('name-moki')} diff --git a/src/ep.config.json b/src/ep.config.json index cee48ee69..567f46935 100644 --- a/src/ep.config.json +++ b/src/ep.config.json @@ -30,6 +30,10 @@ "skuImagesUrl": "https://elasticpath-demo-images.s3-us-west-2.amazonaws.com/VESTRI_VIRTUAL_V1/%sku%.png", "siteImagesUrl": "https://elasticpath-demo-images.s3-us-west-2.amazonaws.com/VESTRI_VIRTUAL_V1/%fileName%", "enableOfflineMode": false, + "imageFileTypes": { + "enable": true, + "types": [".png" ,".jpg", ".svg"] + }, "b2b": { "enable": false, "authServiceAPI": { diff --git a/src/tests/e2e/common.js b/src/tests/e2e/common.js index 37d039bcf..448b3de25 100644 --- a/src/tests/e2e/common.js +++ b/src/tests/e2e/common.js @@ -157,6 +157,7 @@ module.exports = { await page.type(EXPIRY_MONTH, paymentMethod.expiryMonth); await page.type(EXPIRY_YEAR, paymentMethod.expiryYear); await page.type(SECURITY_CODE, paymentMethod.securityCode); + await page.waitFor(3000); await page.click(CONTINUE_BUTTON); }, diff --git a/src/tests/e2e/purchase.test.js b/src/tests/e2e/purchase.test.js index dcc58fc8f..800480a12 100644 --- a/src/tests/e2e/purchase.test.js +++ b/src/tests/e2e/purchase.test.js @@ -54,11 +54,11 @@ async function getChartItems(page) { return chartItems; } -const CART_LINK_CSS = '.cart-link'; +const CART_LINK_CSS = '.cart-link-container .cart-link'; const HOME_PAGE_CSS = 'div.home-page-component'; const REGISTER_BUTTON_CSS = "div[data-region='checkoutAutRegisterOptionRegion'] button.checkout-auth-option-register-btn"; const CHECKOUT_BUTTON_CSS = "button[class='ep-btn primary btn-cmd-checkout']"; -const ADD_NEW_ADDRESS_CSS = 'button[class="ep-btn primary wide checkout-new-address-btn"]'; +const ADD_NEW_ADDRESS_CSS = 'button[data-region="billingAddressButtonRegion"]'; const ADD_NEW_PAYMENT_CSS = 'button[class="ep-btn primary wide new-payment-btn"]'; const ANONYMOUS_EMAIL_INPUT_CSS = 'div[data-region="anonymousCheckoutFeedbackRegion"] ~div input[id="Email"]'; const CHECKOUT_AUTH_BUTTON_CSS = 'button[class="ep-btn primary wide checkout-auth-option-anonymous-checkout-btn"]'; @@ -113,6 +113,7 @@ describe('Purchase feature', () => { await page.waitFor(2000); await page.waitForSelector(CART_LINK_CSS); + await page.waitFor(2000); await page.click(CART_LINK_CSS); await page.waitForSelector(CHECKOUT_BUTTON_CSS); @@ -434,7 +435,7 @@ describe('Purchase feature', () => { }; await loginUser(page, userInfo); - + await addProductToCart(page, 'Mens', '', 'Wordmark Fitted Hat'); await addProductToCart(page, 'Mens', '', 'Men\'s Soft Shell Jacket');