@riosst100/pwa-marketplace 1.0.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.
Files changed (26) hide show
  1. package/package.json +16 -0
  2. package/src/Utilities/graphQL.js +76 -0
  3. package/src/componentOverrideMapping.js +11 -0
  4. package/src/components/Header/websiteSwitcher.js +109 -0
  5. package/src/components/Header/websiteSwitcher.module.css +111 -0
  6. package/src/components/Header/websiteSwitcher.shimmer.js +6 -0
  7. package/src/components/Header/websiteSwitcherItem.js +47 -0
  8. package/src/components/Header/websiteSwitcherItem.module.css +20 -0
  9. package/src/index.js +7 -0
  10. package/src/intercept.js +50 -0
  11. package/src/moduleOverrideWebpackPlugin.js +71 -0
  12. package/src/overwrites/peregrine/lib/talons/Adapter/useAdapter.js +208 -0
  13. package/src/overwrites/peregrine/lib/talons/Header/storeSwitcher.gql.js +45 -0
  14. package/src/overwrites/peregrine/lib/talons/Header/useStoreSwitcher.js +204 -0
  15. package/src/overwrites/pwa-buildpack/lib/queries/getAvailableStoresConfigData.graphql +11 -0
  16. package/src/overwrites/venia-ui/lib/components/Adapter/adapter.js +110 -0
  17. package/src/overwrites/venia-ui/lib/components/Header/header.js +116 -0
  18. package/src/overwrites/venia-ui/lib/components/Header/storeSwitcher.js +118 -0
  19. package/src/overwrites/venia-ui/lib/components/Header/switcherItem.js +47 -0
  20. package/src/overwrites/venia-ui/lib/components/StoreCodeRoute/storeCodeRoute.js +75 -0
  21. package/src/queries/getAvailableWebsitesConfigData.graphql +14 -0
  22. package/src/queries/index.js +30 -0
  23. package/src/talons/Header/useWebsiteSwitcher.js +219 -0
  24. package/src/talons/Header/websiteSwitcher.gql.js +45 -0
  25. package/src/talons/WebsiteByIp/getWebsiteByIp.gql.js +14 -0
  26. package/src/talons/WebsiteByIp/useWebsiteByIp.js +33 -0
@@ -0,0 +1,208 @@
1
+ import { ApolloLink } from '@apollo/client';
2
+ import { InMemoryCache } from '@apollo/client/cache';
3
+ import { ApolloClient } from '@apollo/client/core';
4
+ import { CachePersistor } from 'apollo-cache-persist';
5
+ import { useCallback, useEffect, useMemo, useState } from 'react';
6
+
7
+ import attachClient from '@magento/peregrine/lib/Apollo/attachClientToStore';
8
+ import { clearCartDataFromCache } from '@magento/peregrine/lib/Apollo/clearCartDataFromCache';
9
+ import { clearCustomerDataFromCache } from '@magento/peregrine/lib/Apollo/clearCustomerDataFromCache';
10
+ import { CACHE_PERSIST_PREFIX } from '@magento/peregrine/lib/Apollo/constants';
11
+ import getLinks from '@magento/peregrine/lib/Apollo/links';
12
+ import typePolicies from '@magento/peregrine/lib/Apollo/policies';
13
+ import { BrowserPersistence } from '@magento/peregrine/lib/util';
14
+
15
+ const isServer = !globalThis.document;
16
+ const storage = new BrowserPersistence();
17
+
18
+ export const useAdapter = props => {
19
+ const { apiUrl, configureLinks, origin, store, styles } = props;
20
+
21
+ // Custom
22
+ const websiteCodes = [];
23
+
24
+ AVAILABLE_WEBSITES.forEach(store => {
25
+ websiteCodes.push(store.website_code);
26
+ });
27
+
28
+ websiteCodes.sort((a, b) => b.length - a.length);
29
+
30
+ const { location } = globalThis;
31
+ const match = location && location.pathname.split("/")[1];
32
+ let websiteCodeInUrl = websiteCodes.find((str) => str === match);
33
+
34
+ let isBaseWebsite = false;
35
+
36
+ let websiteCode = storage.getItem('website_code');
37
+
38
+ if (!websiteCodeInUrl) {
39
+ isBaseWebsite = true;
40
+ }
41
+
42
+ let basename = '/';
43
+
44
+ const urlHasStoreCode = isBaseWebsite ? false : true;
45
+ if (urlHasStoreCode) {
46
+ basename = `/${websiteCode}`;
47
+ }
48
+
49
+ const [initialized, setInitialized] = useState(false);
50
+
51
+ const apiBase = useMemo(
52
+ () => apiUrl || new URL('/graphql', origin).toString(),
53
+ [apiUrl, origin]
54
+ );
55
+
56
+ const apolloLink = useMemo(() => {
57
+ let links = getLinks(apiBase);
58
+
59
+ if (configureLinks) {
60
+ links = configureLinks(links, apiBase);
61
+ }
62
+
63
+ return ApolloLink.from(Array.from(links.values()));
64
+ }, [apiBase, configureLinks]);
65
+
66
+ const createApolloClient = useCallback((cache, link) => {
67
+ return new ApolloClient({
68
+ cache,
69
+ link,
70
+ ssrMode: isServer
71
+ });
72
+ }, []);
73
+
74
+ const createCachePersistor = useCallback((storeCode, cache) => {
75
+ return isServer
76
+ ? null
77
+ : new CachePersistor({
78
+ key: `${CACHE_PERSIST_PREFIX}-${storeCode}`,
79
+ cache,
80
+ storage: globalThis.localStorage,
81
+ debug: process.env.NODE_ENV === 'development'
82
+ });
83
+ }, []);
84
+
85
+ const clearCacheData = useCallback(
86
+ async (client, cacheType) => {
87
+ const storeCode = storage.getItem('store_view_code') || 'default';
88
+
89
+ // Clear current store
90
+ if (cacheType === 'cart') {
91
+ await clearCartDataFromCache(client);
92
+ } else if (cacheType === 'customer') {
93
+ await clearCustomerDataFromCache(client);
94
+ }
95
+
96
+ // Clear other stores
97
+ for (const store of AVAILABLE_STORE_VIEWS) {
98
+ if (store.store_code !== storeCode) {
99
+ // Get saved data directly from local storage
100
+ const existingStorePersistor = globalThis.localStorage.getItem(
101
+ `${CACHE_PERSIST_PREFIX}-${store.store_code}`
102
+ );
103
+
104
+ // Make sure we have data available
105
+ if (
106
+ existingStorePersistor &&
107
+ Object.keys(existingStorePersistor).length > 0
108
+ ) {
109
+ const storeCache = new InMemoryCache();
110
+
111
+ // Restore available data
112
+ storeCache.restore(JSON.parse(existingStorePersistor));
113
+
114
+ const storeClient = createApolloClient(
115
+ storeCache,
116
+ apolloLink
117
+ );
118
+
119
+ storeClient.persistor = isServer
120
+ ? null
121
+ : createCachePersistor(
122
+ store.store_code,
123
+ storeCache
124
+ );
125
+
126
+ // Clear other store
127
+ if (cacheType === 'cart') {
128
+ await clearCartDataFromCache(storeClient);
129
+ } else if (cacheType === 'customer') {
130
+ await clearCustomerDataFromCache(storeClient);
131
+ }
132
+ }
133
+ }
134
+ }
135
+ },
136
+ [apolloLink, createApolloClient, createCachePersistor]
137
+ );
138
+
139
+ const apolloClient = useMemo(() => {
140
+ const storeCode = storage.getItem('store_view_code') || 'default';
141
+ const client = createApolloClient(preInstantiatedCache, apolloLink);
142
+ const persistor = isServer
143
+ ? null
144
+ : createCachePersistor(storeCode, preInstantiatedCache);
145
+
146
+ client.apiBase = apiBase;
147
+ client.persistor = persistor;
148
+ client.clearCacheData = clearCacheData;
149
+
150
+ return client;
151
+ }, [
152
+ apiBase,
153
+ apolloLink,
154
+ clearCacheData,
155
+ createApolloClient,
156
+ createCachePersistor
157
+ ]);
158
+
159
+ const getUserConfirmation = useCallback(async (message, callback) => {
160
+ if (typeof globalThis.handleRouteChangeConfirmation === 'function') {
161
+ return globalThis.handleRouteChangeConfirmation(message, callback);
162
+ }
163
+
164
+ return callback(globalThis.confirm(message));
165
+ }, []);
166
+
167
+ const apolloProps = { client: apolloClient };
168
+ const reduxProps = { store };
169
+ const routerProps = { basename, getUserConfirmation };
170
+ const styleProps = { initialState: styles };
171
+
172
+ // perform blocking async work here
173
+ useEffect(() => {
174
+ if (initialized) return;
175
+
176
+ // immediately invoke this async function
177
+ (async () => {
178
+ // restore persisted data to the Apollo cache
179
+ await apolloClient.persistor.restore();
180
+
181
+ // attach the Apollo client to the Redux store
182
+ await attachClient(apolloClient);
183
+
184
+ // mark this routine as complete
185
+ setInitialized(true);
186
+ })();
187
+ }, [apolloClient, initialized]);
188
+
189
+ return {
190
+ apolloProps,
191
+ initialized,
192
+ reduxProps,
193
+ routerProps,
194
+ styleProps,
195
+ urlHasStoreCode
196
+ };
197
+ };
198
+
199
+ /**
200
+ * To improve initial load time, create an apollo cache object as soon as
201
+ * this module is executed, since it doesn't depend on any component props.
202
+ * The tradeoff is that we may be creating an instance we don't end up needing.
203
+ */
204
+ const preInstantiatedCache = new InMemoryCache({
205
+ // POSSIBLE_TYPES is injected into the bundle by webpack at build time.
206
+ possibleTypes: POSSIBLE_TYPES,
207
+ typePolicies
208
+ });
@@ -0,0 +1,45 @@
1
+ import { gql } from '@apollo/client';
2
+
3
+ export const GET_STORE_CONFIG_DATA = gql`
4
+ query getStoreConfigData {
5
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
6
+ storeConfig {
7
+ store_code
8
+ store_name
9
+ store_group_name
10
+ website_code
11
+ }
12
+ }
13
+ `;
14
+
15
+ export const GET_ROUTE_DATA = gql`
16
+ query getRouteData($url: String!) {
17
+ route(url: $url) {
18
+ relative_url
19
+ }
20
+ }
21
+ `;
22
+
23
+ export const GET_AVAILABLE_STORES_DATA = gql`
24
+ query getAvailableStoresData {
25
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
26
+ availableStores {
27
+ default_display_currency_code
28
+ locale
29
+ secure_base_media_url
30
+ store_code
31
+ store_group_code
32
+ store_group_name
33
+ store_name
34
+ store_sort_order
35
+ website_code
36
+ is_default_store
37
+ }
38
+ }
39
+ `;
40
+
41
+ export default {
42
+ getStoreConfigData: GET_STORE_CONFIG_DATA,
43
+ getRouteData: GET_ROUTE_DATA,
44
+ getAvailableStoresData: GET_AVAILABLE_STORES_DATA
45
+ };
@@ -0,0 +1,204 @@
1
+ import { useQuery } from '@apollo/client';
2
+ import { useCallback, useMemo } from 'react';
3
+ import { useLocation } from 'react-router-dom';
4
+ import { useDropdown } from '@magento/peregrine/lib/hooks/useDropdown';
5
+ import { useAwaitQuery } from '@magento/peregrine/lib/hooks/useAwaitQuery';
6
+ import { BrowserPersistence } from '@magento/peregrine/lib/util';
7
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
8
+ import DEFAULT_OPERATIONS from '@magento/peregrine/lib/talons/Header/storeSwitcher.gql';
9
+
10
+ const storage = new BrowserPersistence();
11
+
12
+ const mapAvailableOptions = (config, stores) => {
13
+ const { store_code: configCode } = config;
14
+
15
+ return stores.reduce((map, store) => {
16
+ const {
17
+ default_display_currency_code: currency,
18
+ locale,
19
+ secure_base_media_url,
20
+ store_code: storeCode,
21
+ website_code: websiteCode,
22
+ store_group_code: storeGroupCode,
23
+ store_group_name: storeGroupName,
24
+ store_name: storeName,
25
+ store_sort_order: sortOrder
26
+ } = store;
27
+
28
+ const isCurrent = storeCode === configCode;
29
+ const option = {
30
+ currency,
31
+ isCurrent,
32
+ locale,
33
+ secure_base_media_url,
34
+ sortOrder,
35
+ storeCode,
36
+ websiteCode,
37
+ storeGroupCode,
38
+ storeGroupName,
39
+ storeName
40
+ };
41
+
42
+ return map.set(storeCode, option);
43
+ }, new Map());
44
+ };
45
+
46
+ /**
47
+ * The useStoreSwitcher talon complements the StoreSwitcher component.
48
+ *
49
+ * @param {Array<Object>} [props.availableRoutes] - Hardcoded app routes.
50
+ * @param {Object} [props.operations] - GraphQL operations to be run by the hook.
51
+ *
52
+ * @returns {Map} talonProps.availableStores - Details about the available store views.
53
+ * @returns {String} talonProps.currentStoreName - Name of the current store view.
54
+ * @returns {Boolean} talonProps.storeMenuIsOpen - Whether the menu that this trigger toggles is open or not.
55
+ * @returns {Ref} talonProps.storeMenuRef - A React ref to the menu that this trigger toggles.
56
+ * @returns {Ref} talonProps.storeMenuTriggerRef - A React ref to the trigger element itself.
57
+ * @returns {Function} talonProps.handleTriggerClick - A function for handling when the trigger is clicked.
58
+ * @returns {Function} talonProps.handleSwitchStore - A function for handling when the menu item is clicked.
59
+ */
60
+
61
+ export const useStoreSwitcher = (props = {}) => {
62
+ const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
63
+ const { availableRoutes = [] } = props;
64
+ const internalRoutes = useMemo(() => {
65
+ return availableRoutes.map(path => {
66
+ if (path.exact) {
67
+ return path.pattern;
68
+ }
69
+ });
70
+ }, [availableRoutes]);
71
+
72
+ const {
73
+ getStoreConfigData,
74
+ getRouteData,
75
+ getAvailableStoresData
76
+ } = operations;
77
+ const { pathname, search: searchParams } = useLocation();
78
+ const {
79
+ elementRef: storeMenuRef,
80
+ expanded: storeMenuIsOpen,
81
+ setExpanded: setStoreMenuIsOpen,
82
+ triggerRef: storeMenuTriggerRef
83
+ } = useDropdown();
84
+
85
+ const { data: storeConfigData } = useQuery(getStoreConfigData, {
86
+ fetchPolicy: 'cache-and-network',
87
+ nextFetchPolicy: 'cache-first'
88
+ });
89
+
90
+ const fetchRouteData = useAwaitQuery(getRouteData);
91
+
92
+ const { data: availableStoresData } = useQuery(getAvailableStoresData, {
93
+ fetchPolicy: 'cache-and-network',
94
+ nextFetchPolicy: 'cache-first'
95
+ });
96
+
97
+ const currentStoreName = useMemo(() => {
98
+ if (storeConfigData) {
99
+ return storeConfigData.storeConfig.store_name;
100
+ }
101
+ }, [storeConfigData]);
102
+
103
+ const currentGroupName = useMemo(() => {
104
+ if (storeConfigData) {
105
+ return storeConfigData.storeConfig.store_group_name;
106
+ }
107
+ }, [storeConfigData]);
108
+
109
+ // availableStores => mapped options or empty map if undefined.
110
+ const availableStores = useMemo(() => {
111
+ return (
112
+ (storeConfigData &&
113
+ availableStoresData &&
114
+ mapAvailableOptions(
115
+ storeConfigData.storeConfig,
116
+ availableStoresData.availableStores
117
+ )) ||
118
+ new Map()
119
+ );
120
+ }, [storeConfigData, availableStoresData]);
121
+
122
+ // Create a map of sorted store views for each group.
123
+ const storeGroups = useMemo(() => {
124
+ const groups = new Map();
125
+
126
+ availableStores.forEach(store => {
127
+ const groupCode = store.storeGroupCode;
128
+ if (!groups.has(groupCode)) {
129
+ const groupViews = [store];
130
+ groups.set(groupCode, groupViews);
131
+ } else {
132
+ const groupViews = groups.get(groupCode);
133
+ // Insert store at configured position
134
+ groupViews.splice(store.sortOrder, 0, store);
135
+ }
136
+ });
137
+
138
+ return groups;
139
+ }, [availableStores]);
140
+
141
+ const getPathname = useCallback(
142
+ async storeCode => {
143
+ if (pathname === '' || pathname === '/') return '';
144
+ let newPath = '';
145
+ if (internalRoutes.includes(pathname)) {
146
+ newPath = pathname;
147
+ } else {
148
+ const { data: routeData } = await fetchRouteData({
149
+ fetchPolicy: 'no-cache',
150
+ variables: {
151
+ url: pathname
152
+ },
153
+ context: { headers: { store: storeCode } }
154
+ });
155
+ if (routeData.route) {
156
+ newPath = routeData.route.relative_url;
157
+ }
158
+ }
159
+ return newPath.startsWith('/') ? newPath.substr(1) : newPath;
160
+ },
161
+ [pathname, fetchRouteData, internalRoutes]
162
+ );
163
+
164
+ const handleSwitchStore = useCallback(
165
+ // Change store view code and currency to be used in Apollo link request headers
166
+ async (storeCode, websiteCode) => {
167
+ // Do nothing when store view is not present in available stores
168
+ if (!availableStores.has(storeCode)) return;
169
+
170
+ storage.setItem('store_view_code', storeCode);
171
+ storage.setItem('website_code', websiteCode);
172
+ storage.setItem(
173
+ 'store_view_currency',
174
+ availableStores.get(storeCode).currency
175
+ );
176
+ storage.setItem(
177
+ 'store_view_secure_base_media_url',
178
+ availableStores.get(storeCode).secure_base_media_url
179
+ );
180
+ const pathName = await getPathname(storeCode);
181
+ const newPath = pathName ? `/${pathName}${searchParams}` : '';
182
+
183
+ globalThis.location.assign(`/${websiteCode}${newPath || ''}`);
184
+ },
185
+ [availableStores, getPathname, searchParams]
186
+ );
187
+
188
+ const handleTriggerClick = useCallback(() => {
189
+ // Toggle Stores Menu.
190
+ setStoreMenuIsOpen(isOpen => !isOpen);
191
+ }, [setStoreMenuIsOpen]);
192
+
193
+ return {
194
+ availableStores,
195
+ currentGroupName,
196
+ currentStoreName,
197
+ storeGroups,
198
+ storeMenuRef,
199
+ storeMenuTriggerRef,
200
+ storeMenuIsOpen,
201
+ handleTriggerClick,
202
+ handleSwitchStore
203
+ };
204
+ };
@@ -0,0 +1,11 @@
1
+ query availableStoresConfigData {
2
+ availableStores {
3
+ store_code
4
+ id
5
+ secure_base_media_url
6
+ store_name
7
+ default_display_currency_code
8
+ website_code
9
+ is_default_store
10
+ }
11
+ }
@@ -0,0 +1,110 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { ApolloProvider } from '@apollo/client';
3
+ import { Provider as ReduxProvider } from 'react-redux';
4
+ import { BrowserRouter } from 'react-router-dom';
5
+ import { useAdapter } from '@magento/peregrine/lib/talons/Adapter/useAdapter';
6
+ import App, { AppContextProvider } from '@magento/venia-ui/lib/components/App';
7
+ import StoreCodeRoute from '@magento/venia-ui/lib/components/StoreCodeRoute';
8
+ import { useWebsiteByIp } from '@riosst100/pwa-multi-website/src/talons/WebsiteByIp/useWebsiteByIp';
9
+ import { BrowserPersistence } from '@magento/peregrine/lib/util';
10
+
11
+ const storage = new BrowserPersistence();
12
+
13
+ const Adapter = props => {
14
+ const [verifyUserIp, setVerifyUserIp] = useState(true);
15
+ const [newRouterProps, setNewRouterProps] = useState(true);
16
+
17
+ const talonProps = useAdapter(props);
18
+ let {
19
+ apolloProps,
20
+ initialized,
21
+ reduxProps,
22
+ routerProps
23
+ } = talonProps;
24
+
25
+ const websiteCodes = [];
26
+ const websiteStores = useMemo(() => ({}), []);
27
+ const storeCurrencies = useMemo(() => ({}), []);
28
+ const storeSecureBaseMediaUrl = useMemo(() => ({}), []);
29
+
30
+ AVAILABLE_WEBSITES.forEach(store => {
31
+ websiteCodes.push(store.website_code);
32
+ websiteStores[store.website_code] = store.store_code;
33
+ storeCurrencies[store.store_code] = store.default_display_currency_code;
34
+ storeSecureBaseMediaUrl[store.store_code] = store.secure_base_media_url;
35
+ });
36
+
37
+ websiteCodes.sort((a, b) => b.length - a.length);
38
+
39
+ const { location } = globalThis;
40
+ const match = location && location.pathname.split("/")[1];
41
+ let websiteCodeInUrl = websiteCodes.find((str) => str === match);
42
+
43
+ const { getWebsiteByUserIp } = useWebsiteByIp(websiteCodeInUrl);
44
+
45
+ useEffect(() => {
46
+ if (websiteCodeInUrl) {
47
+ setVerifyUserIp(false);
48
+
49
+ storage.removeItem('access_base_website');
50
+
51
+ routerProps.basename = websiteCodeInUrl != process.env.WEBSITE_CODE ? '/'+websiteCodeInUrl : '/';
52
+
53
+ setNewRouterProps(routerProps);
54
+ }
55
+ }, [websiteCodeInUrl])
56
+
57
+ useEffect(() => {
58
+ if (!websiteCodeInUrl && getWebsiteByUserIp) {
59
+ setVerifyUserIp(false);
60
+
61
+ const query = new URLSearchParams(location.search);
62
+ const isAccessBaseWebsite = query.get('access_base_website') || (
63
+ storage.getItem('access_base_website') || null
64
+ );
65
+
66
+ const websiteCode = getWebsiteByUserIp.countryCode && !isAccessBaseWebsite ? getWebsiteByUserIp.countryCode.toLowerCase() : process.env.WEBSITE_CODE;
67
+
68
+ routerProps.basename = websiteCode != process.env.WEBSITE_CODE && !isAccessBaseWebsite ? '/'+websiteCode : '/';
69
+
70
+ setNewRouterProps(routerProps);
71
+
72
+ if (isAccessBaseWebsite) {
73
+ storage.setItem('access_base_website', 1);
74
+ }
75
+
76
+ let storeCodeInUrl = websiteStores[websiteCode];
77
+
78
+ storage.setItem('store_view_code', storeCodeInUrl);
79
+ storage.setItem('website_code', websiteCode);
80
+ storage.setItem(
81
+ 'store_view_currency',
82
+ storeCurrencies[storeCodeInUrl]
83
+ );
84
+ storage.setItem(
85
+ 'store_view_secure_base_media_url',
86
+ storeSecureBaseMediaUrl[storeCodeInUrl]
87
+ );
88
+ }
89
+ }, [websiteCodeInUrl, getWebsiteByUserIp])
90
+
91
+ // TODO: Replace with app skeleton. See PWA-547.
92
+ if (!initialized) {
93
+ return null;
94
+ }
95
+
96
+ const children = props.children || <App />;
97
+
98
+ return !verifyUserIp && (
99
+ <ApolloProvider {...apolloProps}>
100
+ <ReduxProvider {...reduxProps}>
101
+ <BrowserRouter {...newRouterProps}>
102
+ <StoreCodeRoute />
103
+ <AppContextProvider>{children}</AppContextProvider>
104
+ </BrowserRouter>
105
+ </ReduxProvider>
106
+ </ApolloProvider>
107
+ );
108
+ };
109
+
110
+ export default Adapter;
@@ -0,0 +1,116 @@
1
+ import React, { Fragment, Suspense } from 'react';
2
+ import { shape, string } from 'prop-types';
3
+ import { Link, Route } from 'react-router-dom';
4
+
5
+ import Logo from '@magento/venia-ui/lib/components/Logo';
6
+ import AccountTrigger from '@magento/venia-ui/lib/components/Header/accountTrigger';
7
+ import CartTrigger from '@magento/venia-ui/lib/components/Header/cartTrigger';
8
+ import NavTrigger from '@magento/venia-ui/lib/components/Header/navTrigger';
9
+ import SearchTrigger from '@magento/venia-ui/lib/components/Header/searchTrigger';
10
+ import OnlineIndicator from '@magento/venia-ui/lib/components/Header/onlineIndicator';
11
+ import { useHeader } from '@magento/peregrine/lib/talons/Header/useHeader';
12
+ import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
13
+
14
+ import { useStyle } from '@magento/venia-ui/lib/classify';
15
+ import defaultClasses from '@magento/venia-ui/lib/components/Header/header.module.css';
16
+ import StoreSwitcher from '@magento/venia-ui/lib/components/Header/storeSwitcher';
17
+ import WebsiteSwitcher from '@riosst100/pwa-multi-website/src/components/Header/websiteSwitcher';
18
+ import CurrencySwitcher from '@magento/venia-ui/lib/components/Header/currencySwitcher';
19
+ import MegaMenu from '@magento/venia-ui/lib/components/MegaMenu';
20
+ import PageLoadingIndicator from '@magento/venia-ui/lib/components/PageLoadingIndicator';
21
+ import { useIntl } from 'react-intl';
22
+
23
+ const SearchBar = React.lazy(() => import('@magento/venia-ui/lib/components/SearchBar'));
24
+
25
+ const Header = props => {
26
+ const {
27
+ handleSearchTriggerClick,
28
+ hasBeenOffline,
29
+ isOnline,
30
+ isSearchOpen,
31
+ searchRef,
32
+ searchTriggerRef
33
+ } = useHeader();
34
+
35
+ const classes = useStyle(defaultClasses, props.classes);
36
+ const rootClass = isSearchOpen ? classes.open : classes.closed;
37
+
38
+ const searchBarFallback = (
39
+ <div className={classes.searchFallback} ref={searchRef}>
40
+ <div className={classes.input}>
41
+ <div className={classes.loader}>
42
+ <div className={classes.loaderBefore} />
43
+ <div className={classes.loaderAfter} />
44
+ </div>
45
+ </div>
46
+ </div>
47
+ );
48
+ const searchBar = isSearchOpen ? (
49
+ <Suspense fallback={searchBarFallback}>
50
+ <Route>
51
+ <SearchBar isOpen={isSearchOpen} ref={searchRef} />
52
+ </Route>
53
+ </Suspense>
54
+ ) : null;
55
+
56
+ const { formatMessage } = useIntl();
57
+ const title = formatMessage({ id: 'logo.title', defaultMessage: 'Venia' });
58
+
59
+ return (
60
+ <Fragment>
61
+ <div className={classes.switchersContainer}>
62
+ <div className={classes.switchers} data-cy="Header-switchers">
63
+ <WebsiteSwitcher />
64
+ <StoreSwitcher />
65
+ <CurrencySwitcher />
66
+ </div>
67
+ </div>
68
+ <header className={rootClass} data-cy="Header-root">
69
+ <div className={classes.toolbar}>
70
+ <div className={classes.primaryActions}>
71
+ <NavTrigger />
72
+ </div>
73
+
74
+ <Link
75
+ aria-label={title}
76
+ to={resourceUrl('/')}
77
+ className={classes.logoContainer}
78
+ data-cy="Header-logoContainer"
79
+ >
80
+ <Logo classes={{ logo: classes.logo }} />
81
+ </Link>
82
+ <MegaMenu />
83
+ <div className={classes.secondaryActions}>
84
+ <SearchTrigger
85
+ onClick={handleSearchTriggerClick}
86
+ ref={searchTriggerRef}
87
+ />
88
+ <AccountTrigger />
89
+ <CartTrigger />
90
+ </div>
91
+ </div>
92
+ {searchBar}
93
+ <PageLoadingIndicator absolute />
94
+ </header>
95
+ <OnlineIndicator
96
+ hasBeenOffline={hasBeenOffline}
97
+ isOnline={isOnline}
98
+ />
99
+ </Fragment>
100
+ );
101
+ };
102
+
103
+ Header.propTypes = {
104
+ classes: shape({
105
+ closed: string,
106
+ logo: string,
107
+ open: string,
108
+ primaryActions: string,
109
+ secondaryActions: string,
110
+ toolbar: string,
111
+ switchers: string,
112
+ switchersContainer: string
113
+ })
114
+ };
115
+
116
+ export default Header;