Skip to content

Commit 414cb66

Browse files
Shopper Agent: Control floating button visibility and fix programmatic launch when hidden @W-20615482 (#3651)
* feat: control floating button visibility and fix launch when hidden
1 parent 17d98be commit 414cb66

9 files changed

Lines changed: 124 additions & 16 deletions

File tree

‎packages/pwa-kit-create-app/assets/bootstrap/js/config/default.js.hbs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ module.exports = {
3434
commerceOrgId: '',
3535
siteId: '',
3636
enableConversationContext: 'false',
37-
conversationContext: []
37+
conversationContext: [],
38+
enableAgentFromHeader: 'false',
39+
enableAgentFromFloatingButton: 'false'
3840
},
3941
// Customize how your 'site' and 'locale' are displayed in the url.
4042
url: {

‎packages/pwa-kit-create-app/assets/templates/@salesforce/retail-react-app/config/default.js.hbs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ module.exports = {
3434
commerceOrgId: '',
3535
siteId: '',
3636
enableConversationContext: 'false',
37-
conversationContext: []
37+
conversationContext: [],
38+
enableAgentFromHeader: 'false',
39+
enableAgentFromFloatingButton: 'false'
3840
},
3941
// Customize settings for your url
4042
url: {

‎packages/template-retail-react-app/app/components/shopper-agent/index.jsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ const ShopperAgentWindow = ({commerceAgentConfiguration, domainUrl}) => {
175175
commerceOrgId,
176176
siteId,
177177
enableConversationContext = 'false',
178-
conversationContext = []
178+
conversationContext = [],
179+
enableAgentFromFloatingButton = 'true'
179180
} = commerceAgentConfiguration
180181

181182
// User session identifier hook
@@ -352,7 +353,8 @@ const ShopperAgentWindow = ({commerceAgentConfiguration, domainUrl}) => {
352353
embeddedServiceEndpoint,
353354
scrt2Url,
354355
locale.id,
355-
refreshToken
356+
refreshToken,
357+
enableAgentFromFloatingButton
356358
)
357359

358360
// This component doesn't render visible UI, only manages the messaging service

‎packages/template-retail-react-app/app/components/shopper-agent/index.test.js‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,31 @@ describe('ShopperAgent Component', () => {
509509
'https://test.salesforce.com', // embeddedServiceEndpoint
510510
'https://test.salesforce.com/scrt2.js', // scrt2Url
511511
'en-US', // locale.id
512-
'test-refresh-token' // refreshToken
512+
'test-refresh-token', // refreshToken
513+
'true' // enableAgentFromFloatingButton (default)
514+
)
515+
})
516+
517+
test('should call useMiaw with enableAgentFromFloatingButton false when configured', () => {
518+
const props = {
519+
...defaultProps,
520+
commerceAgentConfiguration: {
521+
...commerceAgentSettings,
522+
enableAgentFromFloatingButton: 'false'
523+
}
524+
}
525+
526+
render(<ShopperAgent {...props} />)
527+
528+
expect(mockedUseMiaw).toHaveBeenCalledWith(
529+
{loaded: true, error: false},
530+
'test-org-id',
531+
'test-service',
532+
'https://test.salesforce.com',
533+
'https://test.salesforce.com/scrt2.js',
534+
'en-US',
535+
'test-refresh-token',
536+
'false' // enableAgentFromFloatingButton
513537
)
514538
})
515539

‎packages/template-retail-react-app/app/hooks/use-miaw.js‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,15 @@ const normalizeLocaleToSalesforce = (locale) => {
7373
* @param {string} embeddedServiceDeploymentUrl - URL of the embedded service deployment
7474
* @param {string} scrt2Url - SCRT2 URL for the embedded messaging service
7575
* @param {string} locale - BCP-47 locale for the embedded messaging service
76+
* @param {string} [enableAgentFromFloatingButton='true'] - When 'false', hides the floating chat button on load
7677
*/
7778
const initEmbeddedMessaging = (
7879
salesforceOrgId,
7980
embeddedServiceDeploymentName,
8081
embeddedServiceDeploymentUrl,
8182
scrt2Url,
82-
locale
83+
locale,
84+
enableAgentFromFloatingButton = 'true'
8385
) => {
8486
try {
8587
if (
@@ -93,6 +95,9 @@ const initEmbeddedMessaging = (
9395
window.embeddedservice_bootstrap.settings.language = salesforceLanguage
9496
window.embeddedservice_bootstrap.settings.disableStreamingResponses = true
9597
window.embeddedservice_bootstrap.settings.enableUserInputForConversationWithBot = false
98+
// Hide floating chat button when enableAgentFromFloatingButton is 'false'
99+
window.embeddedservice_bootstrap.settings.hideChatButtonOnLoad =
100+
enableAgentFromFloatingButton !== 'true'
96101
window.embeddedservice_bootstrap.init(
97102
salesforceOrgId,
98103
embeddedServiceDeploymentName,
@@ -117,6 +122,7 @@ const initEmbeddedMessaging = (
117122
* @param {string} scrt2Url - SCRT2 URL for the embedded messaging service
118123
* @param {string} locale - BCP-47 locale for the embedded messaging service
119124
* @param {string} refreshToken - Refresh token for the embedded messaging service
125+
* @param {string} [enableAgentFromFloatingButton='true'] - When 'false', hides the floating chat button on load
120126
*/
121127
const useMiaw = (
122128
scriptLoadStatus,
@@ -125,7 +131,8 @@ const useMiaw = (
125131
embeddedServiceDeploymentUrl,
126132
scrt2Url,
127133
locale,
128-
refreshToken
134+
refreshToken,
135+
enableAgentFromFloatingButton = 'true'
129136
) => {
130137
useEffect(() => {
131138
if (scriptLoadStatus.loaded && !scriptLoadStatus.error) {
@@ -135,7 +142,7 @@ const useMiaw = (
135142
embeddedServiceDeploymentUrl,
136143
scrt2Url,
137144
locale,
138-
refreshToken
145+
enableAgentFromFloatingButton
139146
)
140147
}
141148
}, [
@@ -145,7 +152,8 @@ const useMiaw = (
145152
embeddedServiceDeploymentUrl,
146153
scrt2Url,
147154
locale,
148-
refreshToken
155+
refreshToken,
156+
enableAgentFromFloatingButton
149157
])
150158
}
151159

‎packages/template-retail-react-app/app/hooks/use-miaw.test.js‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ describe('useMiaw hook', () => {
141141
expect(
142142
window.embeddedservice_bootstrap.settings.enableUserInputForConversationWithBot
143143
).toBe(false)
144+
expect(window.embeddedservice_bootstrap.settings.hideChatButtonOnLoad).toBe(false)
144145
expect(window.embeddedservice_bootstrap.init).toHaveBeenCalledWith(
145146
'test-org-id',
146147
'test-deployment',
@@ -167,6 +168,18 @@ describe('useMiaw hook', () => {
167168
expect(window.embeddedservice_bootstrap.init).not.toHaveBeenCalled()
168169
})
169170

171+
test('should hide floating chat button when enableAgentFromFloatingButton is false', () => {
172+
renderHook(() =>
173+
useMiaw(
174+
mockScriptLoadStatus,
175+
...Object.values(mockParams),
176+
'false' // enableAgentFromFloatingButton
177+
)
178+
)
179+
180+
expect(window.embeddedservice_bootstrap.settings.hideChatButtonOnLoad).toBe(true)
181+
})
182+
170183
test('should handle different locale mappings', () => {
171184
const testCases = [
172185
{locale: 'en-US', expected: 'en_US'},

‎packages/template-retail-react-app/app/utils/config-utils.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export const getCommerceAgentConfig = () => {
2020
siteId: '',
2121
enableConversationContext: 'false',
2222
conversationContext: [],
23-
enableAgentFromHeader: 'false'
23+
enableAgentFromHeader: 'false',
24+
enableAgentFromFloatingButton: 'false'
2425
}
2526
return getConfig().app.commerceAgent ?? defaults
2627
}

‎packages/template-retail-react-app/app/utils/shopper-agent-utils.js‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,27 @@ const onClient = typeof window !== 'undefined'
99
/**
1010
* Launch the chat using the embedded service bootstrap API
1111
*
12+
* When the floating chat button is hidden (hideChatButtonOnLoad=true), this function
13+
* first shows the chat button via utilAPI.showChatButton() before launching the chat,
14+
* ensuring the chat window opens correctly.
15+
*
1216
* @function launchChat
1317
* @returns {void}
1418
*/
1519
export function launchChat() {
1620
if (!onClient) return
1721

1822
try {
19-
// Launch chat using the embedded service bootstrap API
20-
if (
21-
window.embeddedservice_bootstrap?.utilAPI &&
22-
typeof window.embeddedservice_bootstrap.utilAPI.launchChat === 'function'
23-
) {
24-
window.embeddedservice_bootstrap.utilAPI.launchChat()
23+
const utilAPI = window.embeddedservice_bootstrap?.utilAPI
24+
if (!utilAPI) return
25+
const hideChatButtonOnLoad =
26+
window.embeddedservice_bootstrap?.settings?.hideChatButtonOnLoad === true
27+
if (hideChatButtonOnLoad && typeof utilAPI.showChatButton === 'function') {
28+
utilAPI.showChatButton()
29+
}
30+
31+
if (typeof utilAPI.launchChat === 'function') {
32+
utilAPI.launchChat()
2533
}
2634
} catch (error) {
2735
console.error('Shopper Agent: Error launching chat', error)

‎packages/template-retail-react-app/app/utils/shopper-agent-utils.test.js‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,54 @@ describe('shopper-agent-utils', () => {
8080
expect(() => launchChat()).not.toThrow()
8181
})
8282

83+
test('should call showChatButton before launchChat when hideChatButtonOnLoad is true', () => {
84+
const mockShowChatButton = jest.fn()
85+
const mockLaunchChat = jest.fn()
86+
87+
global.window = {
88+
embeddedservice_bootstrap: {
89+
settings: {
90+
hideChatButtonOnLoad: true
91+
},
92+
utilAPI: {
93+
showChatButton: mockShowChatButton,
94+
launchChat: mockLaunchChat
95+
}
96+
}
97+
}
98+
99+
launchChat()
100+
101+
expect(mockShowChatButton).toHaveBeenCalledTimes(1)
102+
expect(mockLaunchChat).toHaveBeenCalledTimes(1)
103+
// showChatButton must be called before launchChat
104+
expect(mockShowChatButton.mock.invocationCallOrder[0]).toBeLessThan(
105+
mockLaunchChat.mock.invocationCallOrder[0]
106+
)
107+
})
108+
109+
test('should not call showChatButton when hideChatButtonOnLoad is false', () => {
110+
const mockShowChatButton = jest.fn()
111+
const mockLaunchChat = jest.fn()
112+
113+
global.window = {
114+
embeddedservice_bootstrap: {
115+
settings: {
116+
hideChatButtonOnLoad: false
117+
},
118+
utilAPI: {
119+
showChatButton: mockShowChatButton,
120+
launchChat: mockLaunchChat
121+
}
122+
}
123+
}
124+
125+
launchChat()
126+
127+
expect(mockShowChatButton).not.toHaveBeenCalled()
128+
expect(mockLaunchChat).toHaveBeenCalledTimes(1)
129+
})
130+
83131
test('should handle errors and log error', () => {
84132
const mockLaunchChat = jest.fn(() => {
85133
throw new Error('Launch error')

0 commit comments

Comments
 (0)