Skip to content
Merged
11 changes: 9 additions & 2 deletions packages/template-retail-react-app/app/assets/svg/sparkle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export const SocialPinterestIcon = icon('social-pinterest', {
})
export const SocialTwitterIcon = icon('social-twitter')
export const SocialYoutubeIcon = icon('social-youtube')
export const SparkleIcon = icon('sparkle')
export const SparkleIcon = icon('sparkle', {viewBox: '0 0 20 20'})
export const StoreIcon = icon('store')
export const SignoutIcon = icon('signout')
export const UserIcon = icon('user')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,11 +425,12 @@ const Search = (props) => {
<HideOnDesktop>
<Flex
display={isOpen || searchInputRef?.value?.length > 0 ? 'block' : 'none'}
postion="absolute"
position="absolute"
background="white"
left={0}
right={0}
height="100vh"
overflowX="hidden"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SearchSuggestions component is using negative margins:
marginLeft: -6 while here we're clipping the element with overflow hidden.

We must seek to address the core issue instead of having contradicting css.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The negative margin was to get a full-bleed look inside a padded container, and overflow hidden is used to hide the side effects of that breakout

refactoring the parent container is currently out of scope and this approach is only to meet the UX requirement. Since the feature is behind the perm, it won't impact the standard suggestion box

>
{searchSuggestion.isLoading ? (
<Spinner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,44 @@ import {FormattedMessage} from 'react-intl'
import {SparkleIcon, ChevronRightIcon} from '@salesforce/retail-react-app/app/components/icons'

const AskAssistantBanner = ({onClick, styles}) => {
const handleInteraction = (e) => {
e.preventDefault()
e.stopPropagation()
onClick?.()
}

return (
<Box
{...styles.askAssistantBanner}
as="button"
type="button"
width="full"
textAlign="left"
onClick={onClick}
aria-label="Ask Assistant - Discover, compare and shop smarter with your personal shopping assistant"
onMouseDown={handleInteraction}
onClick={handleInteraction}
aria-label="Ask Shopping Agent - Discover, compare and shop smarter with your personal shopping assistant"
>
<Box {...styles.askAssistantBannerIcon}>
<SparkleIcon boxSize={5} color="gray.800" />
</Box>
<Box {...styles.askAssistantBannerContent}>
<Box {...styles.askAssistantBannerIcon} as={SparkleIcon} boxSize={6} />
<Box>
<Box {...styles.askAssistantBannerTitleRow}>
<Text {...styles.askAssistantBannerTitle}>
<FormattedMessage
defaultMessage="Ask Shopping Agent"
id="search.suggestions.askAssistant.title"
/>
</Text>
<Text {...styles.askAssistantBannerDescription}>
<FormattedMessage
defaultMessage="Discover, compare, and shop smarter with your personal Shopping Agent."
id="search.suggestions.askAssistant.description"
/>
</Text>
<Box {...styles.askAssistantBannerArrow}>
<ChevronRightIcon boxSize={5} color="gray.800" />
</Box>
</Box>
<Text {...styles.askAssistantBannerDescription}>
<FormattedMessage
defaultMessage="Discover, compare, and shop smarter with your personal Shopping Agent."
id="search.suggestions.askAssistant.description"
/>
</Text>
</Box>
<Box {...styles.askAssistantBannerArrow} as={ChevronRightIcon} boxSize={5} />
</Box>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,22 @@ import AskAssistantBanner from '@salesforce/retail-react-app/app/components/sear
const baseStyles = {
askAssistantBanner: {},
askAssistantBannerContent: {},
askAssistantBannerTitleRow: {},
askAssistantBannerIcon: {},
askAssistantBannerTitle: {},
askAssistantBannerDescription: {},
askAssistantBannerArrow: {}
}

test('renders Ask Assistant banner with title and description', () => {
test('renders Ask Shopping Agent banner with title and description', () => {
renderWithProviders(<AskAssistantBanner onClick={jest.fn()} styles={baseStyles} />)

expect(
screen.getByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
).toBeInTheDocument()
expect(screen.getByText('Ask Shopping Agent')).toBeInTheDocument()
expect(screen.getByText((content) => content === 'Ask Shopping Agent')).toBeInTheDocument()
expect(
screen.getByText(/Discover, compare, and shop smarter with your personal Shopping Agent/i)
).toBeInTheDocument()
Expand All @@ -40,7 +41,7 @@ test('calls onClick when banner is clicked', async () => {
renderWithProviders(<AskAssistantBanner onClick={onClick} styles={baseStyles} />)

const button = screen.getByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
await user.click(button)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const baseStyles = {
phraseContainer: {},
askAssistantBanner: {},
askAssistantBannerContent: {},
askAssistantBannerTitleRow: {},
askAssistantBannerIcon: {},
askAssistantBannerTitle: {},
askAssistantBannerDescription: {},
Expand Down Expand Up @@ -144,8 +145,8 @@ test('renders nothing when there are no categories, products, or phrase suggesti
expect(screen.queryByTestId('sf-horizontal-product-suggestions')).not.toBeInTheDocument()
})

describe('Ask Assistant banner', () => {
test('renders Ask Assistant banner when showAskAssistantBanner and onAskAssistantClick are provided', () => {
describe('Ask Shopping Agent banner', () => {
test('renders Ask Shopping Agent banner when showAskAssistantBanner and onAskAssistantClick are provided', () => {
const searchSuggestions = makeSearchSuggestions({
categorySuggestions: [{type: 'category', name: 'Women', link: '/women'}]
})
Expand All @@ -161,12 +162,12 @@ describe('Ask Assistant banner', () => {
)

const banners = screen.getAllByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
expect(banners.length).toBeGreaterThanOrEqual(1)
})

test('does not render Ask Assistant banner when showAskAssistantBanner is false', () => {
test('does not render Ask Shopping Agent banner when showAskAssistantBanner is false', () => {
const searchSuggestions = makeSearchSuggestions({
categorySuggestions: [{type: 'category', name: 'Women', link: '/women'}]
})
Expand All @@ -183,12 +184,12 @@ describe('Ask Assistant banner', () => {

expect(
screen.queryByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
).not.toBeInTheDocument()
})

test('does not render Ask Assistant banner when onAskAssistantClick is not provided', () => {
test('does not render Ask Shopping Agent banner when onAskAssistantClick is not provided', () => {
const searchSuggestions = makeSearchSuggestions({
categorySuggestions: [{type: 'category', name: 'Women', link: '/women'}]
})
Expand All @@ -204,12 +205,12 @@ describe('Ask Assistant banner', () => {

expect(
screen.queryByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
).not.toBeInTheDocument()
})

test('clicking Ask Assistant banner calls onAskAssistantClick', async () => {
test('clicking Ask Shopping Agent banner calls onAskAssistantClick', async () => {
const user = userEvent.setup()
const onAskAssistantClick = jest.fn()
const searchSuggestions = makeSearchSuggestions({
Expand All @@ -227,10 +228,11 @@ describe('Ask Assistant banner', () => {
)

const banner = screen.getAllByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})[0]
await user.click(banner)

expect(onAskAssistantClick).toHaveBeenCalledTimes(1)
// Banner uses both onMouseDown and onClick, so one user click invokes the handler twice
expect(onAskAssistantClick).toHaveBeenCalledTimes(2)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ jest.mock(
<button
type="button"
onClick={props.onAskAssistantClick}
aria-label="Ask Assistant - Discover, compare and shop smarter with your personal shopping assistant"
aria-label="Ask Shopping Agent - Discover, compare and shop smarter with your personal shopping assistant"
>
Ask Assistant
Ask Shopping Agent
</button>
)}
</div>
Expand All @@ -32,7 +32,7 @@ jest.mock(
}
)

test('when no suggestions: renders RecentSearches and shows Ask Assistant banner when enabled with click handler', () => {
test('when no suggestions: renders RecentSearches and shows Ask Shopping Agent banner when enabled with click handler', () => {
renderWithProviders(
<SearchSuggestions
recentSearches={['shoes', 'dress']}
Expand All @@ -46,12 +46,12 @@ test('when no suggestions: renders RecentSearches and shows Ask Assistant banner
expect(screen.getByTestId('sf-suggestion-recent')).toBeInTheDocument()
expect(
screen.getByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
).toBeInTheDocument()
})

test('when no suggestions and enableAgentFromSearchSuggestions false: does not show Ask Assistant banner', () => {
test('when no suggestions and enableAgentFromSearchSuggestions false: does not show Ask Shopping Agent banner', () => {
renderWithProviders(
<SearchSuggestions
recentSearches={['shoes']}
Expand All @@ -65,12 +65,12 @@ test('when no suggestions and enableAgentFromSearchSuggestions false: does not s
expect(screen.getByTestId('sf-suggestion-recent')).toBeInTheDocument()
expect(
screen.queryByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
).not.toBeInTheDocument()
})

test('when no suggestions and onAskAssistantClick not provided: does not show Ask Assistant banner', () => {
test('when no suggestions and onAskAssistantClick not provided: does not show Ask Shopping Agent banner', () => {
renderWithProviders(
<SearchSuggestions
recentSearches={['shoes']}
Expand All @@ -82,7 +82,7 @@ test('when no suggestions and onAskAssistantClick not provided: does not show As

expect(
screen.queryByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
).not.toBeInTheDocument()
})
Expand All @@ -100,7 +100,7 @@ test('enableAgentFromSearchSuggestions string "true" shows banner when no sugges

expect(
screen.getByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
).toBeInTheDocument()
})
Expand All @@ -121,7 +121,7 @@ test('when has suggestions: renders SuggestionSection with showAskAssistantBanne

expect(screen.getByTestId('suggestion-section')).toBeInTheDocument()
const banner = screen.getByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
expect(banner).toBeInTheDocument()
})
Expand All @@ -142,7 +142,7 @@ test('when has suggestions and banner enabled: clicking banner calls onAskAssist
)

const banner = screen.getByRole('button', {
name: /ask assistant.*discover, compare and shop smarter/i
name: /Ask Shopping Agent.*discover, compare and shop smarter/i
})
await user.click(banner)
expect(onAskAssistantClick).toHaveBeenCalledTimes(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,40 +79,71 @@ export default {
left: 2
},
askAssistantBanner: {
borderRadius: 'md',
padding: 4,
marginTop: 3,
backgroundColor: 'blue.50',
// Mobile: rounded top corners, full-bleed into container padding, internal padding 24px (no 100vw = no horizontal scroll)
// Desktop: same full-bleed
borderRadius: {base: 'md', md: 0},
borderTopLeftRadius: {base: 'md', md: 0},
borderTopRightRadius: {base: 'md', md: 0},
padding: {base: '14px 24px', md: '14px 30%'},
margin: 0,
marginBottom: {base: 0, md: -6},
marginLeft: -6,
width: 'calc(100% + 48px)',
boxSizing: 'border-box',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
justifyContent: 'center',
gap: 3,
color: 'gray.800',
minHeight: '56px',
background:
'linear-gradient(to right, #E0EDFC 0%, #E8E5FC 40%, #F5E5ED 70%, #E8E5FC 100%)',
_hover: {
backgroundColor: 'blue.100'
background:
'linear-gradient(to right, #C7DCFC 0%, #DDD6FC 40%, #F0C9DE 70%, #DDD6FC 100%)'
}
},
askAssistantBannerContent: {
display: 'flex',
alignItems: 'flex-start',
gap: 3,
flexDirection: 'column',
flex: 1,
minWidth: 0
minWidth: 0,
padding: 0,
justifyContent: 'center',
alignItems: 'flex-start',
textAlign: 'left'
},
askAssistantBannerTitleRow: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 2,
width: '100%'
},
askAssistantBannerTitle: {
fontWeight: 'bold',
fontSize: 'md'
fontSize: 'md',
lineHeight: 1.25,
color: 'gray.800',
textAlign: 'left'
},
askAssistantBannerDescription: {
fontSize: 'sm',
color: 'gray.600',
marginTop: 1
marginTop: '2px',
textAlign: 'left'
},
askAssistantBannerIcon: {
flexShrink: 0
flexShrink: 0,
padding: 0,
display: 'flex',
alignItems: 'center',
transform: 'translateY(-50%)'
},
askAssistantBannerArrow: {
flexShrink: 0
flexShrink: 0,
display: 'flex',
alignItems: 'center'
}
}
}
Loading