Skip to content

Commit

Permalink
@W-17443086 Allow store to be selected by Shopper (#2187)
Browse files Browse the repository at this point in the history
* add radio buttons for each store in StoreLocator
* save store info in local storage
  • Loading branch information
hajinsuha1 authored Jan 22, 2025
1 parent 710037d commit 3adb5ca
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 81 deletions.
1 change: 1 addition & 0 deletions packages/template-retail-react-app/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- [BUG] Fixed GET /shopper-context API calls being made without the usid [#2206](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2206)
- Update test data references to 2024, and unify to 01/2040 [#2196](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2197)
- Fixed failing checkout tests [#2195](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2195)
- Allow store to be selectable in StoreLocator [#2187](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2187)
- [BUG] Fixed "getCheckboxProps is not a function" when rendering checkout page in generated app.[#2140](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2140)
- Replace transfer basket call with merge basket on checkout [#2138](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2138)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 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 React, {useEffect, useState} from 'react'
import {useIntl} from 'react-intl'
import PropTypes from 'prop-types'

Expand All @@ -15,78 +15,113 @@ import {
AccordionButton,
AccordionIcon,
AccordionPanel,
Box
Box,
HStack,
Radio,
RadioGroup
} from '@salesforce/retail-react-app/app/components/shared/ui'

// Hooks
import useMultiSite from '@salesforce/retail-react-app/app/hooks/use-multi-site'

const StoresList = ({storesInfo}) => {
const intl = useIntl()
const {site} = useMultiSite()
const storeInfoKey = `store_${site.id}`
const [selectedStore, setSelectedStore] = useState('')

return storesInfo?.map((store, index) => {
return (
<AccordionItem key={index}>
<Box margin="10px">
{store.name ? <Box fontSize="lg">{store.name}</Box> : ''}
<Box fontSize="md" color="gray.600">
{store.address1}
</Box>
<Box fontSize="md" color="gray.600">
{store.city}, {store.stateCode ? store.stateCode : ''} {store.postalCode}
</Box>
{store.distance !== undefined ? (
<>
<br />
<Box fontSize="md" color="gray.600">
{store.distance} {store.distanceUnit}{' '}
{intl.formatMessage({
id: 'store_locator.description.away',
defaultMessage: 'away'
})}
</Box>
</>
) : (
''
)}
{store.phone !== undefined ? (
<>
<br />
<Box fontSize="md" color="gray.600">
{intl.formatMessage({
id: 'store_locator.description.phone',
defaultMessage: 'Phone:'
})}{' '}
{store.phone}
</Box>
</>
) : (
''
)}
{store?.storeHours ? (
<>
{' '}
<AccordionButton color="blue.700" style={{marginTop: '10px'}}>
<Box fontSize="lg">
{intl.formatMessage({
id: 'store_locator.action.viewMore',
defaultMessage: 'View More'
})}
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel mb={6} mt={4}>
<div
dangerouslySetInnerHTML={{
__html: store?.storeHours
}}
/>
</AccordionPanel>{' '}
</>
) : (
''
)}
</Box>
</AccordionItem>
useEffect(() => {
setSelectedStore(JSON.parse(window.localStorage.getItem(storeInfoKey))?.id || '')
}, [storeInfoKey])

const handleChange = (storeId) => {
setSelectedStore(storeId)
const store = storesInfo.find((store) => store.id === storeId)
window.localStorage.setItem(
storeInfoKey,
JSON.stringify({
id: storeId,
name: store.name || null,
inventoryId: store.inventoryId || null
})
)
})
}

return (
<RadioGroup onChange={handleChange} value={selectedStore}>
{storesInfo?.map((store, index) => {
return (
<AccordionItem key={index}>
<HStack align="flex-start" mt="16px" mb="16px">
<Radio
value={store.id}
mt="1px"
aria-describedby={`store-info-${store.id}`}
></Radio>
<Box id={`store-info-${store.id}`}>
{store.name && <Box fontSize="lg">{store.name}</Box>}
<Box fontSize="md" color="gray.600">
{store.address1}
</Box>
<Box fontSize="md" color="gray.600">
{store.city}, {store.stateCode ? store.stateCode : ''}{' '}
{store.postalCode}
</Box>
{store.distance !== undefined && (
<>
<br />
<Box fontSize="md" color="gray.600">
{store.distance} {store.distanceUnit}{' '}
{intl.formatMessage({
id: 'store_locator.description.away',
defaultMessage: 'away'
})}
</Box>
</>
)}
{store.phone && (
<>
<br />
<Box fontSize="md" color="gray.600">
{intl.formatMessage({
id: 'store_locator.description.phone',
defaultMessage: 'Phone:'
})}{' '}
{store.phone}
</Box>
</>
)}
{store.storeHours && (
<>
{' '}
<AccordionButton
color="blue.700"
sx={{marginTop: '10px', paddingBottom: '0px'}}
>
<Box fontSize="lg">
{intl.formatMessage({
id: 'store_locator.action.viewMore',
defaultMessage: 'View More'
})}
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel mb={6} mt={4}>
<div
dangerouslySetInnerHTML={{
__html: store?.storeHours
}}
/>
</AccordionPanel>{' '}
</>
)}
</Box>
</HStack>
</AccordionItem>
)
})}
</RadioGroup>
)
}

StoresList.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
import React from 'react'
import StoresList from '@salesforce/retail-react-app/app/components/store-locator-modal/stores-list'
import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
import {waitFor, screen} from '@testing-library/react'
import {waitFor, screen, fireEvent} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import {Accordion} from '@salesforce/retail-react-app/app/components/shared/ui'
import mockConfig from '@salesforce/retail-react-app/config/mocks/default'

const mockSearchStoresData = [
{
Expand Down Expand Up @@ -73,10 +74,9 @@ const mockSearchStoresData = [
distance: 81.1,
distanceUnit: 'km',
id: '00021',
inventoryId: 'inventory_m_store_store13',
latitude: 49.4077,
longitude: 8.6908,
name: 'Heidelberg Tech Mart',
name: 'Store with no inventoryId',
phone: '+49 6221 123456',
posEnabled: false,
postalCode: '69117',
Expand Down Expand Up @@ -104,18 +104,26 @@ describe('StoresList', () => {
</Accordion>
)

expect(screen.queryAllByRole('radio')).toHaveLength(mockSearchStoresData.length)

await waitFor(async () => {
const aStoreName = screen.getByText(/Wiesbaden Tech Depot/i)
const aStoreAddress = screen.getByText(/Kirchgasse 12/i)
const aStoreCityAndPostalCode = screen.getByText(/Wiesbaden, 65185/i)
const aStoreDistance = screen.getByText(/0.74 km away/i)
const aStorePhoneNumber = screen.getByText(/49 611 876543/i)

expect(aStoreName).toBeInTheDocument()
expect(aStoreAddress).toBeInTheDocument()
expect(aStoreCityAndPostalCode).toBeInTheDocument()
expect(aStoreDistance).toBeInTheDocument()
expect(aStorePhoneNumber).toBeInTheDocument()
mockSearchStoresData.forEach((store) => {
const storeName = screen.getByText(store.name)
const storeAddress = screen.getByText(store.address1)
const storeCityAndPostalCode = screen.getByText(
`${store.city}, ${store.postalCode}`
)
const storeDistance = screen.getByText(
`${store.distance} ${store.distanceUnit} away`
)
const storePhoneNumber = screen.getByText(`Phone: ${store.phone}`)

expect(storeName).toBeInTheDocument()
expect(storeAddress).toBeInTheDocument()
expect(storeCityAndPostalCode).toBeInTheDocument()
expect(storeDistance).toBeInTheDocument()
expect(storePhoneNumber).toBeInTheDocument()
})
})
})

Expand Down Expand Up @@ -169,4 +177,23 @@ describe('StoresList', () => {
expect(positions).toEqual([...positions].sort((a, b) => a - b))
})
})

test('Can select store', async () => {
renderWithProviders(
<Accordion>
<StoresList storesInfo={mockSearchStoresData} />
</Accordion>
)

await waitFor(async () => {
const {id, name, inventoryId} = mockSearchStoresData[1]
const radioButton = screen.getByDisplayValue(id)
fireEvent.click(radioButton)

const expectedStoreInfo = {id, name, inventoryId}
expect(localStorage.getItem(`store_${mockConfig.app.defaultSite}`)).toEqual(
JSON.stringify(expectedStoreInfo)
)
})
})
})

0 comments on commit 3adb5ca

Please sign in to comment.