@shopgate/engage
Version:
Shopgate's ENGAGE library.
192 lines (185 loc) • 6.58 kB
JavaScript
import React, { memo, useCallback, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { i18n } from '@shopgate/engage/core';
import { InfoIcon, LocatorIcon, MagnifierIcon, MessageBar, SurroundPortals } from '@shopgate/engage/components';
import { useCountriesNames } from '@shopgate/engage/i18n';
import StoreListSearchRadius from "./StoreListSearchRadius";
import { FulfillmentContext, StoreFinderContext } from "../../locations.context";
import connect from "./StoreListSearch.connector";
import { container, countriesCell, inputCell, radiusCell, inputIcon, iconClass, input, inputContainer, select, selectContainer } from "./StoreListSearch.style";
import { FULFILLMENT_SHEET_SEARCH } from "../../constants/Portals";
/**
* @param {Function} getProductLocations getProductLocations.
* @param {Function} storeSearch .
* @param {Object} search .
* @returns {JSX}
*/
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
function StoreListSearch({
postalCode,
countryCode,
setPostalCode,
setCountryCode,
setGeolocation,
isStoreFinder
}) {
const {
isLoading,
setIsLoading,
locations,
shopSettings: {
supportedCountries
} = {},
product
} = useContext(isStoreFinder ? StoreFinderContext : FulfillmentContext);
const [message, setMessage] = useState('');
const [inputPostalCode, setInputPostalCode] = useState(postalCode || '');
const isMounted = useRef(false);
const productId = product?.id || null;
const inputEl = useRef(null);
useEffect(() => {
isMounted.current = true;
return function cleanup() {
isMounted.current = false;
};
});
useLayoutEffect(() => {
if (!isLoading && (!locations || locations.length === 0)) {
// Set a message when a location search resulted in zero locations.
setMessage('locations.error_no_store_found');
} else {
setMessage('');
}
}, [isLoading, locations, message]);
/**
* Triggers an update when the value of the country selector changed.
* @param {SyntheticEvent} event A React event object.
*/
const handleCountrySelectChange = useCallback(event => {
setCountryCode(event.target.value, productId, isStoreFinder);
}, [isStoreFinder, productId, setCountryCode]);
useEffect(() => {
if (!Array.isArray(supportedCountries) || !supportedCountries.length) {
return;
}
// Check if current countryCode is included in supportedCountries. Update the code to a valid
// one if nothing was found.
if (!supportedCountries.includes(countryCode)) {
handleCountrySelectChange({
target: {
value: supportedCountries[0]
}
});
}
}, [countryCode, handleCountrySelectChange, supportedCountries]);
/**
* Blurs the postal code input to trigger an update.
* @param {SyntheticEvent} event A React event object.
*/
const handlePostalCodeSubmitKeyDown = useCallback(event => {
if (event.keyCode === 13) {
inputEl.current.blur();
}
}, []);
/**
* Triggers an update when the input blurs.
*/
const handlePostalCodeBlur = useCallback(() => {
setPostalCode(inputPostalCode, productId, isStoreFinder);
}, [inputPostalCode, isStoreFinder, productId, setPostalCode]);
/**
* Triggers an update when the locate me button was pressed. Also clears the local state for the
* postal code input.
*/
const handleLocateMeButton = useCallback(async () => {
setInputPostalCode('');
setIsLoading(true);
await setGeolocation({
productId,
isStoreFinder
});
setIsLoading(false);
}, [isStoreFinder, productId, setGeolocation, setIsLoading]);
/**
* Updates the local state for the postal code input.
* @param {SyntheticEvent} event A React event object
*/
const handlePostalCodeChange = event => {
setInputPostalCode(event.target.value);
};
const countries = useCountriesNames(supportedCountries);
const hasSupportedCountries = supportedCountries && supportedCountries.length > 1;
return /*#__PURE__*/_jsxs(SurroundPortals, {
portalName: FULFILLMENT_SHEET_SEARCH,
portalProps: {
product
},
children: [/*#__PURE__*/_jsxs("div", {
className: container,
children: [hasSupportedCountries && /*#__PURE__*/_jsx("div", {
className: countriesCell,
children: /*#__PURE__*/_jsx("div", {
className: selectContainer,
children: /*#__PURE__*/_jsx("select", {
name: "countryCode",
value: countryCode,
onChange: handleCountrySelectChange,
className: select,
children: Object.keys(countries).map(key => /*#__PURE__*/_jsx("option", {
className: "option",
value: key,
children: countries[key]
}, key))
})
})
}), /*#__PURE__*/_jsx("div", {
className: inputCell,
children: /*#__PURE__*/_jsxs("div", {
className: inputContainer,
children: [/*#__PURE__*/_jsx("span", {
className: inputIcon,
"aria-hidden": true,
children: /*#__PURE__*/_jsx(MagnifierIcon, {})
}), /*#__PURE__*/_jsx("input", {
ref: inputEl,
name: "postalCode",
className: input,
value: inputPostalCode,
onChange: handlePostalCodeChange,
onBlur: handlePostalCodeBlur,
onKeyDown: handlePostalCodeSubmitKeyDown,
disabled: isLoading,
type: "search",
autoComplete: "off",
autoCorrect: "off",
placeholder: i18n.text('locations.search_placeholder'),
"aria-label": i18n.text('locations.search_placeholder')
}), /*#__PURE__*/_jsx("button", {
onClick: handleLocateMeButton,
type: "button",
className: inputIcon,
"aria-label": i18n.text('locations.stores_near.location'),
children: /*#__PURE__*/_jsx(LocatorIcon, {})
})]
})
}), /*#__PURE__*/_jsx("div", {
className: radiusCell,
children: isStoreFinder && /*#__PURE__*/_jsx(StoreListSearchRadius, {})
})]
}), message && /*#__PURE__*/_jsx(MessageBar, {
messages: [{
type: 'error',
message,
icon: InfoIcon
}],
classNames: {
icon: iconClass
}
})]
});
}
StoreListSearch.defaultProps = {
postalCode: null,
isStoreFinder: false
};
export default connect(/*#__PURE__*/memo(StoreListSearch));