@salesforce/retail-react-app 8.3.0-nightly-20251208080231 → 8.3.0-preview.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -1
- package/app/components/address-suggestion-dropdown/index.jsx +189 -0
- package/app/components/address-suggestion-dropdown/index.test.jsx +332 -0
- package/app/components/forms/address-fields.jsx +18 -2
- package/app/components/forms/useAddressFields.jsx +139 -1
- package/app/components/forms/useAddressFields.test.js +310 -0
- package/app/hooks/useAutocompleteSuggestions.js +131 -0
- package/app/hooks/useAutocompleteSuggestions.test.js +296 -0
- package/app/mocks/mock-address-suggestions.js +445 -0
- package/app/pages/checkout/index.jsx +4 -2
- package/app/pages/checkout/index.test.js +206 -0
- package/app/pages/checkout/util/checkout-context.js +4 -1
- package/app/pages/checkout/util/google-api-provider.js +45 -0
- package/app/pages/checkout/util/google-api-provider.test.js +395 -0
- package/app/ssr.js +5 -1
- package/app/static/img/GoogleMaps_Logo_Gray_4x.png +0 -0
- package/app/static/translations/compiled/en-GB.json +6 -0
- package/app/static/translations/compiled/en-US.json +6 -0
- package/app/static/translations/compiled/en-XA.json +15 -1
- package/app/theme/components/project/swatch-group.js +1 -1
- package/app/utils/address-suggestions.js +237 -0
- package/config/default.js +4 -1
- package/package.json +9 -8
- package/translations/en-GB.json +3 -0
- package/translations/en-US.json +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
## v8.3.0-
|
|
1
|
+
## v8.3.0-preview.0 (Dec 12, 2025)
|
|
2
2
|
- [Bugfix] Fix Forgot Password link not working from Account Profile password update form [#3493](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3493)
|
|
3
|
+
- Introduce Address Autocompletion feature in the checkout flow, powered by Google Maps Platform [#3071](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3071)
|
|
3
4
|
|
|
4
5
|
## v8.2.0 (Nov 04, 2025)
|
|
5
6
|
- Add support for Rule Based Promotions for Choice of Bonus Products. We are currently supporting only one product level rule based promotion per product [#3418](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3418)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
import React, {useEffect, useRef} from 'react'
|
|
8
|
+
import PropTypes from 'prop-types'
|
|
9
|
+
import {FormattedMessage} from 'react-intl'
|
|
10
|
+
import {getAssetUrl} from '@salesforce/pwa-kit-react-sdk/ssr/universal/utils'
|
|
11
|
+
import {
|
|
12
|
+
Box,
|
|
13
|
+
Flex,
|
|
14
|
+
Text,
|
|
15
|
+
IconButton,
|
|
16
|
+
Spinner,
|
|
17
|
+
Stack,
|
|
18
|
+
Spacer
|
|
19
|
+
} from '@salesforce/retail-react-app/app/components/shared/ui'
|
|
20
|
+
import {CloseIcon, LocationIcon} from '@salesforce/retail-react-app/app/components/icons'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Address Suggestion Dropdown Component
|
|
24
|
+
* Displays Google-powered address suggestions in a dropdown format
|
|
25
|
+
*/
|
|
26
|
+
const AddressSuggestionDropdown = ({
|
|
27
|
+
suggestions = [],
|
|
28
|
+
isLoading = false,
|
|
29
|
+
onClose,
|
|
30
|
+
onSelectSuggestion,
|
|
31
|
+
isVisible = false,
|
|
32
|
+
position = 'absolute'
|
|
33
|
+
}) => {
|
|
34
|
+
const dropdownRef = useRef(null)
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const handleClickOutside = (event) => {
|
|
38
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
39
|
+
onClose()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (isVisible) {
|
|
44
|
+
document.addEventListener('mousedown', handleClickOutside)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return () => {
|
|
48
|
+
document.removeEventListener('mousedown', handleClickOutside)
|
|
49
|
+
}
|
|
50
|
+
}, [isVisible, onClose])
|
|
51
|
+
|
|
52
|
+
if (!isVisible || suggestions.length === 0) {
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isLoading) {
|
|
57
|
+
return (
|
|
58
|
+
<Box
|
|
59
|
+
position="absolute"
|
|
60
|
+
top="100%"
|
|
61
|
+
left={0}
|
|
62
|
+
right={0}
|
|
63
|
+
bg="white"
|
|
64
|
+
border="1px solid"
|
|
65
|
+
borderColor="gray.200"
|
|
66
|
+
borderRadius="md"
|
|
67
|
+
boxShadow="md"
|
|
68
|
+
zIndex={1000}
|
|
69
|
+
p={4}
|
|
70
|
+
>
|
|
71
|
+
<Flex align="center" justify="center">
|
|
72
|
+
<Spinner size="sm" mr={2} />
|
|
73
|
+
<Text>Loading suggestions...</Text>
|
|
74
|
+
</Flex>
|
|
75
|
+
</Box>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Box
|
|
81
|
+
ref={dropdownRef}
|
|
82
|
+
data-testid="address-suggestion-dropdown"
|
|
83
|
+
position={position}
|
|
84
|
+
top="100%"
|
|
85
|
+
left={0}
|
|
86
|
+
right={0}
|
|
87
|
+
zIndex={1000}
|
|
88
|
+
bg="white"
|
|
89
|
+
border="1px solid"
|
|
90
|
+
borderColor="gray.200"
|
|
91
|
+
borderRadius="md"
|
|
92
|
+
boxShadow="md"
|
|
93
|
+
mt={1}
|
|
94
|
+
>
|
|
95
|
+
<Flex px={4} pr={0} py={2} alignItems="center">
|
|
96
|
+
<Text fontSize="sm" fontWeight="medium" color="gray.600">
|
|
97
|
+
<FormattedMessage
|
|
98
|
+
defaultMessage="SUGGESTED"
|
|
99
|
+
id="addressSuggestionDropdown.suggested"
|
|
100
|
+
/>
|
|
101
|
+
</Text>
|
|
102
|
+
<Spacer />
|
|
103
|
+
<IconButton
|
|
104
|
+
size="sm"
|
|
105
|
+
variant="ghost"
|
|
106
|
+
icon={<CloseIcon boxSize={4} color="gray.600" />}
|
|
107
|
+
onClick={onClose}
|
|
108
|
+
aria-label="Close suggestions"
|
|
109
|
+
/>
|
|
110
|
+
</Flex>
|
|
111
|
+
<Stack spacing={0}>
|
|
112
|
+
{suggestions.map((suggestion, index) => (
|
|
113
|
+
<Box
|
|
114
|
+
key={index}
|
|
115
|
+
px={4}
|
|
116
|
+
py={3}
|
|
117
|
+
cursor="pointer"
|
|
118
|
+
_hover={{bg: 'gray.50'}}
|
|
119
|
+
onClick={() => onSelectSuggestion(suggestion)}
|
|
120
|
+
role="button"
|
|
121
|
+
tabIndex={0}
|
|
122
|
+
onKeyDown={(e) => {
|
|
123
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
124
|
+
onSelectSuggestion(suggestion)
|
|
125
|
+
}
|
|
126
|
+
}}
|
|
127
|
+
>
|
|
128
|
+
<Flex alignItems="center" gap={2}>
|
|
129
|
+
<LocationIcon boxSize={4} color="black" />
|
|
130
|
+
<Box flex={1}>
|
|
131
|
+
<Text fontSize="sm" noOfLines={1}>
|
|
132
|
+
{suggestion.description ||
|
|
133
|
+
`${suggestion.structured_formatting?.main_text}, ${suggestion.structured_formatting?.secondary_text}`}
|
|
134
|
+
</Text>
|
|
135
|
+
</Box>
|
|
136
|
+
</Flex>
|
|
137
|
+
</Box>
|
|
138
|
+
))}
|
|
139
|
+
</Stack>
|
|
140
|
+
|
|
141
|
+
<Box px={4} py={3} display="flex" alignItems="center">
|
|
142
|
+
<img
|
|
143
|
+
src={getAssetUrl('static/img/GoogleMaps_Logo_Gray_4x.png')}
|
|
144
|
+
alt="Google Maps"
|
|
145
|
+
style={{width: '98px', height: '18px'}}
|
|
146
|
+
/>
|
|
147
|
+
</Box>
|
|
148
|
+
</Box>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
AddressSuggestionDropdown.propTypes = {
|
|
153
|
+
/** Array of address suggestions to display */
|
|
154
|
+
suggestions: PropTypes.arrayOf(
|
|
155
|
+
PropTypes.shape({
|
|
156
|
+
description: PropTypes.string,
|
|
157
|
+
place_id: PropTypes.string,
|
|
158
|
+
structured_formatting: PropTypes.shape({
|
|
159
|
+
main_text: PropTypes.string,
|
|
160
|
+
secondary_text: PropTypes.string
|
|
161
|
+
}),
|
|
162
|
+
terms: PropTypes.arrayOf(
|
|
163
|
+
PropTypes.shape({
|
|
164
|
+
offset: PropTypes.number,
|
|
165
|
+
value: PropTypes.string
|
|
166
|
+
})
|
|
167
|
+
),
|
|
168
|
+
types: PropTypes.arrayOf(PropTypes.string),
|
|
169
|
+
placePrediction: PropTypes.object
|
|
170
|
+
})
|
|
171
|
+
),
|
|
172
|
+
|
|
173
|
+
/** Whether the dropdown should be visible */
|
|
174
|
+
isVisible: PropTypes.bool,
|
|
175
|
+
|
|
176
|
+
/** Callback when close button is clicked */
|
|
177
|
+
onClose: PropTypes.func.isRequired,
|
|
178
|
+
|
|
179
|
+
/** Callback when a suggestion is selected */
|
|
180
|
+
onSelectSuggestion: PropTypes.func.isRequired,
|
|
181
|
+
|
|
182
|
+
/** CSS position property for the dropdown */
|
|
183
|
+
position: PropTypes.oneOf(['absolute', 'relative', 'fixed']),
|
|
184
|
+
|
|
185
|
+
/** Whether the dropdown is loading */
|
|
186
|
+
isLoading: PropTypes.bool
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export default AddressSuggestionDropdown
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react'
|
|
9
|
+
import {render, screen, fireEvent} from '@testing-library/react'
|
|
10
|
+
import {IntlProvider} from 'react-intl'
|
|
11
|
+
import '@testing-library/jest-dom'
|
|
12
|
+
import AddressSuggestionDropdown from '@salesforce/retail-react-app/app/components/address-suggestion-dropdown/index'
|
|
13
|
+
|
|
14
|
+
describe('AddressSuggestionDropdown', () => {
|
|
15
|
+
const mockSuggestions = [
|
|
16
|
+
{
|
|
17
|
+
description: '123 Main Street, New York, NY 10001, USA',
|
|
18
|
+
place_id: 'ChIJ1234567890',
|
|
19
|
+
structured_formatting: {
|
|
20
|
+
main_text: '123 Main Street',
|
|
21
|
+
secondary_text: 'New York, NY 10001, USA'
|
|
22
|
+
},
|
|
23
|
+
terms: [
|
|
24
|
+
{value: '123 Main Street'},
|
|
25
|
+
{value: 'New York'},
|
|
26
|
+
{value: 'NY'},
|
|
27
|
+
{value: '10001'},
|
|
28
|
+
{value: 'USA'}
|
|
29
|
+
],
|
|
30
|
+
placePrediction: {
|
|
31
|
+
text: {text: '123 Main Street, New York, NY 10001, USA'},
|
|
32
|
+
placeId: 'ChIJ1234567890'
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
description: '456 Oak Avenue, Los Angeles, CA 90210, USA',
|
|
37
|
+
place_id: 'ChIJ4567890123',
|
|
38
|
+
structured_formatting: {
|
|
39
|
+
main_text: '456 Oak Avenue',
|
|
40
|
+
secondary_text: 'Los Angeles, CA 90210, USA'
|
|
41
|
+
},
|
|
42
|
+
terms: [
|
|
43
|
+
{value: '456 Oak Avenue'},
|
|
44
|
+
{value: 'Los Angeles'},
|
|
45
|
+
{value: 'CA'},
|
|
46
|
+
{value: '90210'},
|
|
47
|
+
{value: 'USA'}
|
|
48
|
+
],
|
|
49
|
+
placePrediction: {
|
|
50
|
+
text: {text: '456 Oak Avenue, Los Angeles, CA 90210, USA'},
|
|
51
|
+
placeId: 'ChIJ4567890123'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
const defaultProps = {
|
|
57
|
+
suggestions: [],
|
|
58
|
+
isLoading: false,
|
|
59
|
+
isVisible: false,
|
|
60
|
+
onClose: jest.fn(),
|
|
61
|
+
onSelectSuggestion: jest.fn()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const renderWithIntl = (component) => {
|
|
65
|
+
return render(
|
|
66
|
+
<IntlProvider locale="en" messages={{}}>
|
|
67
|
+
{component}
|
|
68
|
+
</IntlProvider>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
jest.clearAllMocks()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('should not render when isVisible is false', () => {
|
|
77
|
+
renderWithIntl(<AddressSuggestionDropdown {...defaultProps} />)
|
|
78
|
+
|
|
79
|
+
expect(screen.queryByTestId('address-suggestion-dropdown')).not.toBeInTheDocument()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should render dropdown when isVisible is true', () => {
|
|
83
|
+
renderWithIntl(
|
|
84
|
+
<AddressSuggestionDropdown
|
|
85
|
+
{...defaultProps}
|
|
86
|
+
isVisible={true}
|
|
87
|
+
suggestions={mockSuggestions}
|
|
88
|
+
/>
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
expect(screen.getByTestId('address-suggestion-dropdown')).toBeInTheDocument()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should render loading state when isLoading is true', () => {
|
|
95
|
+
renderWithIntl(
|
|
96
|
+
<AddressSuggestionDropdown
|
|
97
|
+
{...defaultProps}
|
|
98
|
+
isVisible={true}
|
|
99
|
+
isLoading={true}
|
|
100
|
+
suggestions={[
|
|
101
|
+
{
|
|
102
|
+
description: 'dummy',
|
|
103
|
+
place_id: 'dummy',
|
|
104
|
+
structured_formatting: {
|
|
105
|
+
main_text: 'dummy',
|
|
106
|
+
secondary_text: 'dummy'
|
|
107
|
+
},
|
|
108
|
+
terms: [],
|
|
109
|
+
placePrediction: {
|
|
110
|
+
text: {text: 'dummy'},
|
|
111
|
+
placeId: 'dummy'
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
]}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
expect(screen.getByText('Loading suggestions...')).toBeInTheDocument()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should render suggestions when provided', () => {
|
|
122
|
+
renderWithIntl(
|
|
123
|
+
<AddressSuggestionDropdown
|
|
124
|
+
{...defaultProps}
|
|
125
|
+
isVisible={true}
|
|
126
|
+
suggestions={mockSuggestions}
|
|
127
|
+
/>
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
expect(screen.getByText('123 Main Street, New York, NY 10001, USA')).toBeInTheDocument()
|
|
131
|
+
expect(screen.getByText('456 Oak Avenue, Los Angeles, CA 90210, USA')).toBeInTheDocument()
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('should call onSelectSuggestion when a suggestion is clicked', () => {
|
|
135
|
+
const mockOnSelect = jest.fn()
|
|
136
|
+
renderWithIntl(
|
|
137
|
+
<AddressSuggestionDropdown
|
|
138
|
+
{...defaultProps}
|
|
139
|
+
isVisible={true}
|
|
140
|
+
suggestions={mockSuggestions}
|
|
141
|
+
onSelectSuggestion={mockOnSelect}
|
|
142
|
+
/>
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
fireEvent.click(screen.getByText('123 Main Street, New York, NY 10001, USA'))
|
|
146
|
+
|
|
147
|
+
expect(mockOnSelect).toHaveBeenCalledWith(mockSuggestions[0])
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('should call onSelectSuggestion when Enter key is pressed on a suggestion', () => {
|
|
151
|
+
const mockOnSelect = jest.fn()
|
|
152
|
+
renderWithIntl(
|
|
153
|
+
<AddressSuggestionDropdown
|
|
154
|
+
{...defaultProps}
|
|
155
|
+
isVisible={true}
|
|
156
|
+
suggestions={mockSuggestions}
|
|
157
|
+
onSelectSuggestion={mockOnSelect}
|
|
158
|
+
/>
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
const firstSuggestion = screen
|
|
162
|
+
.getByText('123 Main Street, New York, NY 10001, USA')
|
|
163
|
+
.closest('[role="button"]')
|
|
164
|
+
fireEvent.keyDown(firstSuggestion, {key: 'Enter', code: 'Enter'})
|
|
165
|
+
|
|
166
|
+
expect(mockOnSelect).toHaveBeenCalledWith(mockSuggestions[0])
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should call onClose when close button is clicked', () => {
|
|
170
|
+
const mockOnClose = jest.fn()
|
|
171
|
+
renderWithIntl(
|
|
172
|
+
<AddressSuggestionDropdown
|
|
173
|
+
{...defaultProps}
|
|
174
|
+
isVisible={true}
|
|
175
|
+
suggestions={mockSuggestions}
|
|
176
|
+
onClose={mockOnClose}
|
|
177
|
+
/>
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
const closeButton = screen.getByLabelText('Close suggestions')
|
|
181
|
+
fireEvent.click(closeButton)
|
|
182
|
+
|
|
183
|
+
expect(mockOnClose).toHaveBeenCalled()
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('should handle empty suggestions array', () => {
|
|
187
|
+
renderWithIntl(
|
|
188
|
+
<AddressSuggestionDropdown {...defaultProps} isVisible={true} suggestions={[]} />
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
expect(screen.queryByTestId('address-suggestion-dropdown')).not.toBeInTheDocument()
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('should handle suggestions with missing secondaryText', () => {
|
|
195
|
+
const suggestionsWithoutSecondary = [
|
|
196
|
+
{
|
|
197
|
+
description: '123 Main Street',
|
|
198
|
+
place_id: 'ChIJ1234567890',
|
|
199
|
+
structured_formatting: {
|
|
200
|
+
main_text: '123 Main Street',
|
|
201
|
+
secondary_text: null
|
|
202
|
+
},
|
|
203
|
+
terms: [{offset: 0, value: '123 Main Street'}],
|
|
204
|
+
types: ['street_address']
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
renderWithIntl(
|
|
209
|
+
<AddressSuggestionDropdown
|
|
210
|
+
{...defaultProps}
|
|
211
|
+
isVisible={true}
|
|
212
|
+
suggestions={suggestionsWithoutSecondary}
|
|
213
|
+
/>
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
expect(screen.getByText('123 Main Street')).toBeInTheDocument()
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('should handle keyboard navigation', () => {
|
|
220
|
+
const mockOnSelect = jest.fn()
|
|
221
|
+
renderWithIntl(
|
|
222
|
+
<AddressSuggestionDropdown
|
|
223
|
+
{...defaultProps}
|
|
224
|
+
isVisible={true}
|
|
225
|
+
suggestions={mockSuggestions}
|
|
226
|
+
onSelectSuggestion={mockOnSelect}
|
|
227
|
+
/>
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
const firstSuggestion = screen
|
|
231
|
+
.getByText('123 Main Street, New York, NY 10001, USA')
|
|
232
|
+
.closest('[role="button"]')
|
|
233
|
+
fireEvent.keyDown(firstSuggestion, {key: 'Enter', code: 'Enter'})
|
|
234
|
+
|
|
235
|
+
expect(mockOnSelect).toHaveBeenCalledWith(mockSuggestions[0])
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it('should handle mouse hover on suggestions', () => {
|
|
239
|
+
const mockOnSelect = jest.fn()
|
|
240
|
+
renderWithIntl(
|
|
241
|
+
<AddressSuggestionDropdown
|
|
242
|
+
{...defaultProps}
|
|
243
|
+
isVisible={true}
|
|
244
|
+
suggestions={mockSuggestions}
|
|
245
|
+
onSelectSuggestion={mockOnSelect}
|
|
246
|
+
/>
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
const firstSuggestion = screen
|
|
250
|
+
.getByText('123 Main Street, New York, NY 10001, USA')
|
|
251
|
+
.closest('[role="button"]')
|
|
252
|
+
|
|
253
|
+
// Verify element exists before hover
|
|
254
|
+
expect(firstSuggestion).toBeInTheDocument()
|
|
255
|
+
|
|
256
|
+
// Simulate hover events
|
|
257
|
+
fireEvent.mouseEnter(firstSuggestion)
|
|
258
|
+
fireEvent.mouseLeave(firstSuggestion)
|
|
259
|
+
|
|
260
|
+
// Verify element is still present and functional after hover
|
|
261
|
+
expect(firstSuggestion).toBeInTheDocument()
|
|
262
|
+
expect(firstSuggestion).toHaveAttribute('role', 'button')
|
|
263
|
+
|
|
264
|
+
// Verify the suggestion is still clickable after hover
|
|
265
|
+
fireEvent.click(firstSuggestion)
|
|
266
|
+
expect(mockOnSelect).toHaveBeenCalledWith(mockSuggestions[0])
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('should display Google Maps placePrediction data correctly', () => {
|
|
270
|
+
const googleMapsSuggestions = [
|
|
271
|
+
{
|
|
272
|
+
description: '123 Main St, New York, NY 10001, USA',
|
|
273
|
+
place_id: 'test-place-id',
|
|
274
|
+
structured_formatting: {
|
|
275
|
+
main_text: '123 Main St',
|
|
276
|
+
secondary_text: 'New York, NY 10001, USA'
|
|
277
|
+
},
|
|
278
|
+
terms: [
|
|
279
|
+
{value: '123 Main St'},
|
|
280
|
+
{value: 'New York'},
|
|
281
|
+
{value: 'NY'},
|
|
282
|
+
{value: '10001'},
|
|
283
|
+
{value: 'USA'}
|
|
284
|
+
],
|
|
285
|
+
placePrediction: {
|
|
286
|
+
text: {text: '123 Main St, New York, NY 10001, USA'},
|
|
287
|
+
placeId: 'test-place-id'
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
renderWithIntl(
|
|
293
|
+
<AddressSuggestionDropdown
|
|
294
|
+
{...defaultProps}
|
|
295
|
+
isVisible={true}
|
|
296
|
+
suggestions={googleMapsSuggestions}
|
|
297
|
+
/>
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
expect(screen.getByText('123 Main St, New York, NY 10001, USA')).toBeInTheDocument()
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('should fallback to structured_formatting when placePrediction is not available', () => {
|
|
304
|
+
const fallbackSuggestions = [
|
|
305
|
+
{
|
|
306
|
+
description: '123 Main St, New York, NY 10001, USA',
|
|
307
|
+
place_id: 'test-place-id',
|
|
308
|
+
structured_formatting: {
|
|
309
|
+
main_text: '123 Main St',
|
|
310
|
+
secondary_text: 'New York, NY 10001, USA'
|
|
311
|
+
},
|
|
312
|
+
terms: [
|
|
313
|
+
{value: '123 Main St'},
|
|
314
|
+
{value: 'New York'},
|
|
315
|
+
{value: 'NY'},
|
|
316
|
+
{value: '10001'},
|
|
317
|
+
{value: 'USA'}
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
|
|
322
|
+
renderWithIntl(
|
|
323
|
+
<AddressSuggestionDropdown
|
|
324
|
+
{...defaultProps}
|
|
325
|
+
isVisible={true}
|
|
326
|
+
suggestions={fallbackSuggestions}
|
|
327
|
+
/>
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
expect(screen.getByText('123 Main St, New York, NY 10001, USA')).toBeInTheDocument()
|
|
331
|
+
})
|
|
332
|
+
})
|
|
@@ -11,12 +11,14 @@ import {
|
|
|
11
11
|
Grid,
|
|
12
12
|
GridItem,
|
|
13
13
|
SimpleGrid,
|
|
14
|
-
Stack
|
|
14
|
+
Stack,
|
|
15
|
+
Box
|
|
15
16
|
} from '@salesforce/retail-react-app/app/components/shared/ui'
|
|
16
17
|
import useAddressFields from '@salesforce/retail-react-app/app/components/forms/useAddressFields'
|
|
17
18
|
import Field from '@salesforce/retail-react-app/app/components/field'
|
|
18
19
|
import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer'
|
|
19
20
|
import {MESSAGE_PROPTYPE} from '@salesforce/retail-react-app/app/utils/locale'
|
|
21
|
+
import AddressSuggestionDropdown from '@salesforce/retail-react-app/app/components/address-suggestion-dropdown'
|
|
20
22
|
|
|
21
23
|
const defaultFormTitleAriaLabel = defineMessage({
|
|
22
24
|
defaultMessage: 'Address Form',
|
|
@@ -51,7 +53,21 @@ const AddressFields = ({
|
|
|
51
53
|
</SimpleGrid>
|
|
52
54
|
<Field {...fields.phone} />
|
|
53
55
|
<Field {...fields.countryCode} />
|
|
54
|
-
|
|
56
|
+
|
|
57
|
+
<Box position="relative">
|
|
58
|
+
<Field {...fields.address1} />
|
|
59
|
+
<AddressSuggestionDropdown
|
|
60
|
+
suggestions={fields.address1.autocomplete.suggestions}
|
|
61
|
+
isVisible={
|
|
62
|
+
fields.address1.autocomplete.showDropdown &&
|
|
63
|
+
!fields.address1.autocomplete.isDismissed
|
|
64
|
+
}
|
|
65
|
+
onClose={fields.address1.autocomplete.onClose}
|
|
66
|
+
onSelectSuggestion={fields.address1.autocomplete.onSelectSuggestion}
|
|
67
|
+
position="absolute"
|
|
68
|
+
/>
|
|
69
|
+
</Box>
|
|
70
|
+
|
|
55
71
|
<Field {...fields.city} />
|
|
56
72
|
<Grid templateColumns="repeat(8, 1fr)" gap={5}>
|
|
57
73
|
<GridItem colSpan={[4, 4, 4]}>
|