diff --git a/packages/extension-chakra-store-locator/package.json b/packages/extension-chakra-store-locator/package.json
index 20440f26a1..30120082d3 100644
--- a/packages/extension-chakra-store-locator/package.json
+++ b/packages/extension-chakra-store-locator/package.json
@@ -27,7 +27,8 @@
"prop-types": "^15",
"react": "^18",
"react-dom": "^18",
- "react-router-dom": "^5.3.4"
+ "react-router-dom": "^5.3.4",
+ "react-hook-form": "^7"
},
"peerDependenciesMeta": {
"@chakra-ui/react": {
@@ -53,6 +54,9 @@
},
"react-dom": {
"optional": true
+ },
+ "react-hook-form": {
+ "optional": true
}
},
"devDependencies": {
diff --git a/packages/extension-chakra-store-locator/src/components/with-store-locator.test.jsx b/packages/extension-chakra-store-locator/src/components/with-store-locator.test.jsx
index e2e3e67df9..0a2ad16364 100644
--- a/packages/extension-chakra-store-locator/src/components/with-store-locator.test.jsx
+++ b/packages/extension-chakra-store-locator/src/components/with-store-locator.test.jsx
@@ -8,13 +8,29 @@ import React from 'react'
import {render, screen} from '@testing-library/react'
import {withStoreLocator} from './with-store-locator'
import {useStoreLocator} from './use-store-locator'
+import {useExtensionStore} from '../hooks/use-extension-store'
import PropTypes from 'prop-types'
-// Mock the hook
jest.mock('./use-store-locator', () => ({
useStoreLocator: jest.fn()
}))
+jest.mock('../hooks/use-extension-store', () => ({
+ useExtensionStore: jest.fn()
+}))
+
+// Mock the StoreLocatorModal component
+jest.mock('./modal', () => ({
+ // eslint-disable-next-line react/prop-types
+ StoreLocatorModal: ({isOpen, onClose}) =>
+ isOpen ? (
+
+ Modal Content
+
+
+ ) : null
+}))
+
describe('withStoreLocator', () => {
const mockConfig = {
defaultCountryCode: 'US',
@@ -25,12 +41,18 @@ describe('withStoreLocator', () => {
supportedCountries: []
}
+ const mockStore = {
+ isModalOpen: false,
+ closeModal: jest.fn()
+ }
+
beforeEach(() => {
useStoreLocator.mockReturnValue({
searchStoresParams: {},
setSearchStoresParams: jest.fn(),
config: mockConfig
})
+ useExtensionStore.mockReturnValue(mockStore)
})
it('wraps component with StoreLocatorProvider', () => {
@@ -67,4 +89,47 @@ describe('withStoreLocator', () => {
expect(WrappedComponent.displayName).toBe('WithStoreLocator(TestComponent)')
})
+
+ it('renders modal when isModalOpen is true', () => {
+ const TestComponent = () => Test Component
+ const WrappedComponent = withStoreLocator(TestComponent, mockConfig)
+
+ useExtensionStore.mockReturnValue({
+ ...mockStore,
+ isModalOpen: true
+ })
+
+ render()
+ expect(screen.getByTestId('store-locator-modal')).toBeTruthy()
+ })
+
+ it('does not render modal when isModalOpen is false', () => {
+ const TestComponent = () => Test Component
+ const WrappedComponent = withStoreLocator(TestComponent, mockConfig)
+
+ useExtensionStore.mockReturnValue({
+ ...mockStore,
+ isModalOpen: false
+ })
+
+ render()
+ expect(screen.queryByTestId('store-locator-modal')).toBeNull()
+ })
+
+ it('calls closeModal when modal is closed', () => {
+ const TestComponent = () => Test Component
+ const WrappedComponent = withStoreLocator(TestComponent, mockConfig)
+ const mockCloseModal = jest.fn()
+
+ useExtensionStore.mockReturnValue({
+ isModalOpen: true,
+ closeModal: mockCloseModal
+ })
+
+ render()
+ const closeButton = screen.getByText('Close')
+ closeButton.click()
+
+ expect(mockCloseModal).toHaveBeenCalled()
+ })
})
diff --git a/packages/extension-chakra-store-locator/src/components/with-store-locator.tsx b/packages/extension-chakra-store-locator/src/components/with-store-locator.tsx
index 955287f7ba..38d78d598b 100644
--- a/packages/extension-chakra-store-locator/src/components/with-store-locator.tsx
+++ b/packages/extension-chakra-store-locator/src/components/with-store-locator.tsx
@@ -5,8 +5,10 @@
* 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 {useExtensionStore} from '../hooks/use-extension-store'
import {Config as StoreLocatorConfig} from '../types/config'
import {StoreLocatorProvider} from './provider'
+import {StoreLocatorModal} from './modal'
/**
* Higher-order component that wraps a component with the StoreLocatorProvider
@@ -18,9 +20,12 @@ export const withStoreLocator = (
config: StoreLocatorConfig
): React.ComponentType
=> {
const WithConfig = (props: P) => {
+ const {isModalOpen, closeModal} = useExtensionStore()
+
return (
+
)
}
diff --git a/packages/extension-chakra-store-locator/src/setup-app.ts b/packages/extension-chakra-store-locator/src/setup-app.ts
index c1898113d6..f32d4150cc 100644
--- a/packages/extension-chakra-store-locator/src/setup-app.ts
+++ b/packages/extension-chakra-store-locator/src/setup-app.ts
@@ -27,20 +27,20 @@ import StoreLocatorPage from './pages/store-locator'
import {logger} from './logger'
import extensionMeta from '../extension-meta.json'
-// NOTE: Hey Kevin, this is where you are going to define the type of the store slice for your extension. I imagine that you'll
-// have something that manages the modal being open/closed here.
interface StoreSlice {
- count: number
- increment: () => void
- decrement: () => void
+ isModalOpen: boolean
+ openModal: () => void
+ closeModal: () => void
}
-// This is the store slice definition that we are adding via the `withApplicationExtensionStore` HOC below in the extendApp
-// method.
const storeSliceInitializer: SliceInitializer = (set) => ({
- count: 0,
- increment: () => set((state) => ({count: state.count + 1})),
- decrement: () => set((state) => ({count: state.count - 1}))
+ isModalOpen: false,
+ openModal: () => {
+ set((state) => ({...state, isModalOpen: true}))
+ },
+ closeModal: () => {
+ set((state) => ({...state, isModalOpen: false}))
+ }
})
class StoreLocatorExtension extends ApplicationExtension {
static readonly id = extensionMeta.id
@@ -57,15 +57,15 @@ class StoreLocatorExtension extends ApplicationExtension {
}
const HOCs = [
- (component: React.ComponentType) => withStoreLocator(component, config),
- (component: React.ComponentType) =>
- withOptionalCommerceSdkReactProvider(component, config),
- (component: React.ComponentType) => withOptionalChakra(component),
(component: React.ComponentType) =>
withApplicationExtensionStore(component, {
id: extensionMeta.id,
initializer: storeSliceInitializer
- })
+ }),
+ (component: React.ComponentType) => withStoreLocator(component, config),
+ (component: React.ComponentType) =>
+ withOptionalCommerceSdkReactProvider(component, config),
+ (component: React.ComponentType) => withOptionalChakra(component)
]
return applyHOCs(App, HOCs)
diff --git a/packages/extension-chakra-storefront/src/components/header/index.jsx b/packages/extension-chakra-storefront/src/components/header/index.jsx
index 1798525b16..25a4945c6c 100644
--- a/packages/extension-chakra-storefront/src/components/header/index.jsx
+++ b/packages/extension-chakra-storefront/src/components/header/index.jsx
@@ -28,6 +28,10 @@ import {
useMediaQuery
} from '@chakra-ui/react'
import {AuthHelpers, useAuthHelper, useCustomerType} from '@salesforce/commerce-sdk-react'
+import {
+ useApplicationExtension,
+ useApplicationExtensionsStore
+} from '@salesforce/pwa-kit-extension-sdk/react'
import {useCurrentBasket} from '../../hooks/use-current-basket'
@@ -41,7 +45,8 @@ import {
HamburgerIcon,
ChevronDownIcon,
HeartIcon,
- SignoutIcon
+ SignoutIcon,
+ StoreIcon
} from '../../components/icons'
import {navLinks, messages} from '../../pages/account/constant'
@@ -123,6 +128,13 @@ const Header = ({
onOpen: onAccountMenuOpen
} = useDisclosure()
const [isDesktop] = useMediaQuery('(min-width: 992px)')
+ const storeLocatorExtension = useApplicationExtension(
+ '@salesforce/extension-chakra-store-locator'
+ )
+ const isStoreLocatorEnabled = !!storeLocatorExtension && storeLocatorExtension.isEnabled
+ const {openModal} = useApplicationExtensionsStore((state) => {
+ return state.state['@salesforce/extension-chakra-store-locator'] || {}
+ })
const [showLoading, setShowLoading] = useState(false)
// tracking if users enter the popover Content,
@@ -304,6 +316,20 @@ const Header = ({
{...styles.wishlistIcon}
onClick={onWishlistClick}
/>
+ {isStoreLocatorEnabled && (
+ }
+ {...styles.icons}
+ variant="unstyled"
+ onClick={() => {
+ openModal()
+ }}
+ />
+ )}
{
useMediaQuery: jest.fn().mockReturnValue([true])
}
})
+
+jest.mock('@salesforce/pwa-kit-extension-sdk/react', () => ({
+ ...jest.requireActual('@salesforce/pwa-kit-extension-sdk/react'),
+ useApplicationExtensionsStore: jest.fn().mockReturnValue({
+ isModalOpen: false,
+ closeModal: jest.fn()
+ })
+}))
+
const MockedComponent = ({history}) => {
const onAccountClick = () => {
history.push(createPathWithDefaults('/account'))