Skip to content

Commit 407faa9

Browse files
Merge branch 'feature/manual-bonus-products-v4' into t/cc-sharks/W-19168138/add-new-modal/main
merge conflict in packages/template-chakra-storefront/src/hooks/index.js where the recently added line "export {useManualBonusProducts} from './use-manual-bonus-products'" was not being used as the file doesn't exist. I removed it
1 parent 90b1c1a commit 407faa9

File tree

50 files changed

+850
-1990
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+850
-1990
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,4 @@ jobs:
393393
uses: "./.github/actions/bundle_size_test"
394394
with:
395395
cwd: ${{ env.PROJECT_DIR }}
396-
config: '{"build/main.js": "10kB", "build/vendor.js": "390kB"}'
396+
config: '{"build/main.js": "59kB", "build/vendor.js": "390kB"}'

packages/pwa-kit-create-app/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- Deprecate V3 Extensibility and experimental V4 Extensibility (#2573)
44
- Move extensibility logic to generator (#2573)
55
- Apply prettier to trimmed files (#2688)
6+
- Convert Social Login feature into an extension [#3017](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3017)
67

78
## v3.10.0 (Feb 18, 2025)
89
- Add Data Cloud API configuration to `default.js`. [#2318] (https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2229)

packages/pwa-kit-create-app/assets/plugin-config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@ module.exports = {
1717
// SFDC_EXT_HELLO_WORLD_ENABLED: {
1818
// description: 'The Hello World Extension'
1919
// },
20+
SFDC_EXT_SOCIAL_LOGIN: {
21+
description: 'Social login Extension'
22+
}
2023
}
2124
}

packages/pwa-kit-create-app/assets/templates/chakra-storefront/config/default.js.hbs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,16 @@ module.exports = {
9191
login: {
9292
passwordless: {
9393
enabled: {{#if answers.project.demo.enableDemoSettings}}true{{else}}false{{/if}},
94-
callbackURI: process.env.PASSWORDLESS_LOGIN_CALLBACK_URI || '/passwordless-login-callback',
94+
callbackURI:
95+
process.env.PASSWORDLESS_LOGIN_CALLBACK_URI || '/passwordless-login-callback',
9596
landingPath: '/passwordless-login-landing'
9697
},
98+
{{#if selectedPlugins.SFDC_EXT_SOCIAL_LOGIN}}
9799
social: {
98-
enabled: {{#if answers.project.demo.enableDemoSettings}}true{{else}}false{{/if}},
99100
idps: ['google', 'apple'],
100101
redirectURI: process.env.SOCIAL_LOGIN_REDIRECT_URI || '/social-callback'
101102
},
103+
{{/if}}
102104
resetPassword: {
103105
callbackURI: process.env.RESET_PASSWORD_CALLBACK_URI || '/reset-password-callback',
104106
landingPath: '/reset-password-landing'
@@ -188,13 +190,7 @@ module.exports = {
188190
],
189191
ssrEnabled: true,
190192
ssrOnly: ['ssr.js', 'ssr.js.map', 'node_modules/**/*.*'],
191-
ssrShared: [
192-
'static/favicon.ico',
193-
'static/robots.txt',
194-
'**/*.js',
195-
'**/*.js.map',
196-
'**/*.json'
197-
],
193+
ssrShared: ['static/favicon.ico', 'static/robots.txt', '**/*.js', '**/*.js.map', '**/*.json'],
198194
ssrParameters: {
199195
ssrFunctionNodeVersion: '22.x',
200196
proxyConfigs: [

packages/pwa-kit-create-app/scripts/create-mobify-app.js

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ const PRESETS = [
278278
'project.einstein.siteId': 'aaij-MobileFirst',
279279
'project.dataCloud.appSourceId': '7ae070a6-f4ec-4def-a383-d9cacc3f20a1',
280280
'project.dataCloud.tenantId': 'g82wgnrvm-ywk9dggrrw8mtggy.pc-rnd',
281-
'project.demo.enableDemoSettings': false
281+
'project.demo.enableDemoSettings': false,
282+
'project.selectedPlugins.SFDC_EXT_SOCIAL_LOGIN': false
282283
},
283284
assets: ['translations'],
284285
private: false
@@ -313,7 +314,8 @@ const PRESETS = [
313314
['project.einstein.siteId']: 'aaij-MobileFirst',
314315
['project.dataCloud.appSourceId']: 'fb81edab-24c6-4b40-8684-b67334dfdf32',
315316
['project.dataCloud.tenantId']: 'mmyw8zrxhfsg09lfmzrd1zjqmg',
316-
['project.demo.enableDemoSettings']: true // True only for presets deployed to demo environments like pwa-kit.mobify-storefront.com
317+
['project.demo.enableDemoSettings']: true, // True only for presets deployed to demo environments like pwa-kit.mobify-storefront.com
318+
['project.selectedPlugins.SFDC_EXT_SOCIAL_LOGIN']: false
317319
},
318320
assets: ['translations'],
319321
private: true
@@ -324,7 +326,7 @@ const PRESETS = [
324326
description: '',
325327
templateSource: {
326328
type: TEMPLATE_SOURCE_BUNDLE,
327-
id: 'typescript-minimal'
329+
id: 'chakra-storefront'
328330
},
329331
answers: {
330332
'project.hybrid': false,
@@ -339,7 +341,8 @@ const PRESETS = [
339341
'project.einstein.siteId': 'aaij-MobileFirst',
340342
'project.dataCloud.appSourceId': 'fb81edab-24c6-4b40-8684-b67334dfdf32',
341343
'project.dataCloud.tenantId': 'mmyw8zrxhfsg09lfmzrd1zjqmg',
342-
'project.demo.enableDemoSettings': false
344+
'project.demo.enableDemoSettings': false,
345+
'project.selectedPlugins.SFDC_EXT_SOCIAL_LOGIN': true
343346
},
344347
assets: ['translations'],
345348
private: true
@@ -365,7 +368,8 @@ const PRESETS = [
365368
'project.einstein.siteId': 'aaij-MobileFirst',
366369
'project.dataCloud.appSourceId': 'fb81edab-24c6-4b40-8684-b67334dfdf32',
367370
'project.dataCloud.tenantId': 'mmyw8zrxhfsg09lfmzrd1zjqmg',
368-
'project.demo.enableDemoSettings': false
371+
'project.demo.enableDemoSettings': false,
372+
'project.selectedPlugins.SFDC_EXT_SOCIAL_LOGIN': false
369373
},
370374
assets: ['translations'],
371375
private: true
@@ -391,7 +395,8 @@ const PRESETS = [
391395
'project.dataCloud.appSourceId': 'fb81edab-24c6-4b40-8684-b67334dfdf32',
392396
'project.dataCloud.tenantId': 'mmyw8zrxhfsg09lfmzrd1zjqmg',
393397
'project.commerce.isSlasPrivate': true,
394-
'project.demo.enableDemoSettings': false
398+
'project.demo.enableDemoSettings': false,
399+
'project.selectedPlugins.SFDC_EXT_SOCIAL_LOGIN': false
395400
},
396401
assets: ['translations'],
397402
private: true
@@ -417,7 +422,8 @@ const PRESETS = [
417422
'project.commerce.isSlasPrivate': true,
418423
'project.dataCloud.appSourceId': 'fb81edab-24c6-4b40-8684-b67334dfdf32',
419424
'project.dataCloud.tenantId': 'mmyw8zrxhfsg09lfmzrd1zjqmg',
420-
'project.demo.enableDemoSettings': false
425+
'project.demo.enableDemoSettings': false,
426+
'project.selectedPlugins.SFDC_EXT_SOCIAL_LOGIN': false
421427
},
422428
assets: ['translations'],
423429
private: true
@@ -443,7 +449,8 @@ const PRESETS = [
443449
'project.commerce.isSlasPrivate': false,
444450
'project.dataCloud.appSourceId': 'fb81edab-24c6-4b40-8684-b67334dfdf32',
445451
'project.dataCloud.tenantId': 'mmyw8zrxhfsg09lfmzrd1zjqmg',
446-
'project.demo.enableDemoSettings': false
452+
'project.demo.enableDemoSettings': false,
453+
'project.selectedPlugins.SFDC_EXT_SOCIAL_LOGIN': false
447454
},
448455
assets: ['translations'],
449456
private: true
@@ -660,7 +667,7 @@ const expandKey = (key, value) =>
660667
* const expandedObj = expand({'coolthings.babynames': 'Preseley', 'coolthings.cars': 'bmws'})
661668
* console.log(expandedObj) // {coolthings: { babynames: 'Presley', cars: 'bmws'}}
662669
*
663-
* @param {Object} answers
670+
* @param {Object} answer
664671
* @returns {Object} The expanded object.
665672
*
666673
*/
@@ -884,6 +891,27 @@ const main = async (opts) => {
884891
if (interactive) {
885892
const questions = getQuestions ? getQuestions() : []
886893
const projectAnswers = await prompt(questions, answers)
894+
// Only prompt for plugin selection on interactive presets
895+
if (Object.keys(pluginConfig?.plugins || {}).length > 0) {
896+
const pluginChoices = Object.entries(pluginConfig.plugins).map(([key, config]) => ({
897+
name: config.description,
898+
value: key
899+
}))
900+
901+
const pluginAnswers = await inquirer.prompt([
902+
{
903+
type: 'checkbox',
904+
name: 'selectedPlugins',
905+
message: 'Which extensions would you like to enable?',
906+
choices: pluginChoices
907+
}
908+
])
909+
910+
// Convert selected plugins array to object with true values
911+
pluginAnswers.selectedPlugins.forEach((plugin) => {
912+
selectedPlugins[plugin] = true
913+
})
914+
}
887915
context = merge(context, {
888916
answers: expandObject(projectAnswers)
889917
})
@@ -892,28 +920,14 @@ const main = async (opts) => {
892920
answers: expandObject(answers)
893921
})
894922
}
895-
896-
// Prompt user for plugin selection
897-
if (Object.keys(pluginConfig?.plugins || {}).length > 0) {
898-
const pluginChoices = Object.entries(pluginConfig.plugins).map(([key, config]) => ({
899-
name: config.description,
900-
value: key
901-
}))
902-
903-
const pluginAnswers = await inquirer.prompt([
904-
{
905-
type: 'checkbox',
906-
name: 'selectedPlugins',
907-
message: 'Which extensions would you like to enable?',
908-
choices: pluginChoices
923+
// load plugin selected answer from context object to selectedPlugins (which used for code trimming process)
924+
Object.entries(context.answers?.project?.selectedPlugins || {}).forEach(
925+
([pluginKey, enabled]) => {
926+
if (pluginConfig?.plugins?.[pluginKey]) {
927+
selectedPlugins[pluginKey] = enabled
909928
}
910-
])
911-
912-
// Convert selected plugins array to object with true values
913-
pluginAnswers.selectedPlugins.forEach((plugin) => {
914-
selectedPlugins[plugin] = true
915-
})
916-
}
929+
}
930+
)
917931

918932
if (!OUTPUT_DIR_FLAG_ACTIVE) {
919933
// For extension projects, use the extension name as the output directory

packages/template-chakra-storefront/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
## 0.1.1
22
- Show button "Select Bonus Products" when a product is added, that qualifies the cart for a manual selection bonus product. Also show the corresponding promotional message with the button [#2917](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2917)
3+
- Implemented the "Bonus Product View Modal" [#3043](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3043)
34

45
## 0.1.0-extensibility-preview.5
56
- Fix failing tests in pages folder [#2872](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2872)
67
- Fix component tests in storefront template [#2878](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2878)
78
- Migrate directory structure from `app/*` to `src/*` [#2693](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2693)
89
- Upgrade to Chakra UI v3 and Decomposition on Cart, PLP, PDP and Cart [2839](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2839), [#2872](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2872), [#2878](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2878), [#2924](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2924)
910
- Create a safe version of `<Portal>` that won't break the SSR rendering [#2785](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2785)
11+
- Convert Social Login feature into an extension [#3017](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3017)
12+
1013

1114
## 0.1.0-extensibility-preview.4
1215
- Fix hreflang alternate links [#2269](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2269)

packages/template-chakra-storefront/config/default.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,12 @@ module.exports = {
9595
process.env.PASSWORDLESS_LOGIN_CALLBACK_URI || '/passwordless-login-callback',
9696
landingPath: '/passwordless-login-landing'
9797
},
98+
//@sfdc-extension-block-start SFDC_EXT_SOCIAL_LOGIN
9899
social: {
99-
enabled: false,
100100
idps: ['google', 'apple'],
101101
redirectURI: process.env.SOCIAL_LOGIN_REDIRECT_URI || '/social-callback'
102102
},
103+
//@sfdc-extension-block-end SFDC_EXT_SOCIAL_LOGIN
103104
resetPassword: {
104105
callbackURI: process.env.RESET_PASSWORD_CALLBACK_URI || '/reset-password-callback',
105106
landingPath: '/reset-password-landing'
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright (c) 2021, 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, {useMemo, useCallback} from 'react'
9+
import PropTypes from 'prop-types'
10+
import {Dialog, CloseButton, Button} from '@chakra-ui/react'
11+
import Link from '../link'
12+
import ProductView from '../../components/product-view'
13+
import {useProductViewModal} from '../../hooks/use-product-view-modal'
14+
import SafePortal from '../safe-portal'
15+
import {useIntl} from 'react-intl'
16+
import {productViewModalTheme} from '../../theme/components/project/product-view-modal'
17+
import {useShopperBasketsMutationHelper} from '@salesforce/commerce-sdk-react'
18+
19+
/**
20+
* A Dialog that contains Bonus Product View using product-view-modal theme
21+
*/
22+
const BonusProductViewModal = ({
23+
product,
24+
isOpen,
25+
onClose,
26+
bonusDiscountLineItemId,
27+
promotionId,
28+
...props
29+
}) => {
30+
const productViewModalData = useProductViewModal(product)
31+
const {addItemToNewOrExistingBasket} = useShopperBasketsMutationHelper()
32+
33+
const intl = useIntl()
34+
const {formatMessage} = intl
35+
36+
const messages = useMemo(
37+
() => ({
38+
modalLabel: formatMessage(
39+
{
40+
id: 'bonus_product_view_modal.modal_label',
41+
defaultMessage: 'Bonus product selection modal for {productName}'
42+
},
43+
{productName: productViewModalData?.product?.name}
44+
),
45+
viewCart: formatMessage({
46+
id: 'bonus_product_view_modal.button.view_cart',
47+
defaultMessage: 'View Cart'
48+
})
49+
}),
50+
[intl]
51+
)
52+
53+
// Custom addToCart handler for bonus products that includes bonusDiscountLineItemId
54+
const handleAddToCart = useCallback(
55+
async (variant, quantity) => {
56+
const productItems = [
57+
{
58+
productId: variant?.productId || product?.id,
59+
price: variant?.price || product?.price,
60+
quantity: quantity,
61+
bonusDiscountLineItemId: bonusDiscountLineItemId
62+
}
63+
]
64+
65+
const result = await addItemToNewOrExistingBasket(productItems)
66+
return result
67+
},
68+
[addItemToNewOrExistingBasket, product, bonusDiscountLineItemId]
69+
)
70+
71+
// Custom buttons for the ProductView
72+
const customButtons = useMemo(
73+
() => [
74+
<Button key="view-cart" asChild variant="outline" onClick={onClose}>
75+
<Link to="/cart">{messages.viewCart}</Link>
76+
</Button>
77+
],
78+
[messages.viewCart, onClose]
79+
)
80+
81+
return (
82+
<Dialog.Root
83+
lazyMount
84+
open={isOpen}
85+
onOpenChange={() => onClose()}
86+
size={productViewModalTheme.modal.size}
87+
scrollBehavior={productViewModalTheme.modal.scrollBehavior}
88+
placement={productViewModalTheme.modal.placement}
89+
closeOnInteractOutside={productViewModalTheme.modal.closeOnInteractOutside}
90+
>
91+
<SafePortal>
92+
<Dialog.Backdrop />
93+
<Dialog.Positioner>
94+
<Dialog.Content
95+
data-testid="bonus-product-view-modal"
96+
aria-label={messages.modalLabel}
97+
margin={productViewModalTheme.layout.content.margin}
98+
borderRadius={productViewModalTheme.layout.content.borderRadius}
99+
bgColor={productViewModalTheme.colors.background}
100+
maxHeight={productViewModalTheme.layout.content.maxHeight}
101+
overflowY={productViewModalTheme.layout.content.overflowY}
102+
>
103+
<Dialog.Body
104+
bg={productViewModalTheme.layout.body.background}
105+
padding={productViewModalTheme.layout.body.padding}
106+
paddingBottom={productViewModalTheme.layout.body.paddingBottom}
107+
marginTop={productViewModalTheme.layout.body.marginTop}
108+
>
109+
<ProductView
110+
showFullLink={false}
111+
imageSize={productViewModalTheme.productView.imageSize}
112+
showImageGallery={
113+
productViewModalTheme.productView.showImageGallery
114+
}
115+
product={productViewModalData.product}
116+
isLoading={productViewModalData.isFetching}
117+
addToCart={handleAddToCart}
118+
isProductLoading={productViewModalData.isFetching}
119+
customButtons={customButtons}
120+
promotionId={promotionId}
121+
{...props}
122+
/>
123+
</Dialog.Body>
124+
<Dialog.CloseTrigger asChild>
125+
<CloseButton size="sm" />
126+
</Dialog.CloseTrigger>
127+
</Dialog.Content>
128+
</Dialog.Positioner>
129+
</SafePortal>
130+
</Dialog.Root>
131+
)
132+
}
133+
134+
BonusProductViewModal.propTypes = {
135+
isOpen: PropTypes.bool.isRequired,
136+
onOpen: PropTypes.func,
137+
onClose: PropTypes.func.isRequired,
138+
product: PropTypes.object,
139+
isLoading: PropTypes.bool,
140+
bonusDiscountLineItemId: PropTypes.string, // The 'id' from bonusDiscountLineItems
141+
promotionId: PropTypes.string // The promotion ID to filter promotions in PromoCallout
142+
}
143+
144+
export default BonusProductViewModal

0 commit comments

Comments
 (0)