@sonic-equipment/ui 162.0.0 → 164.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 (60) hide show
  1. package/dist/buttons/link/link.d.ts +2 -1
  2. package/dist/buttons/link/link.js +3 -3
  3. package/dist/cards/orderline-card/orderline-card.js +1 -1
  4. package/dist/collapsables/cascading-component/cascading-component-container-context.d.ts +2 -0
  5. package/dist/collapsables/cascading-component/cascading-component-container-context.js +5 -0
  6. package/dist/collapsables/cascading-component/cascading-component-container-provider.d.ts +7 -0
  7. package/dist/collapsables/cascading-component/cascading-component-container-provider.js +28 -0
  8. package/dist/collapsables/cascading-component/cascading-component-container.d.ts +8 -0
  9. package/dist/collapsables/cascading-component/cascading-component-container.js +8 -0
  10. package/dist/collapsables/cascading-component/cascading-component.d.ts +13 -0
  11. package/dist/collapsables/cascading-component/cascading-component.js +44 -0
  12. package/dist/collapsables/cascading-component/types.d.ts +11 -0
  13. package/dist/collapsables/cascading-component/use-cascading-component-container.d.ts +2 -0
  14. package/dist/collapsables/cascading-component/use-cascading-component-container.js +8 -0
  15. package/dist/collapsables/cascading-component/use-cascading-component.d.ts +2 -0
  16. package/dist/collapsables/cascading-component/use-cascading-component.js +14 -0
  17. package/dist/collapsables/cascading-component/use-has-cascading-component-container.d.ts +1 -0
  18. package/dist/collapsables/cascading-component/use-has-cascading-component-container.js +7 -0
  19. package/dist/collapsables/unmounter/unmounter.d.ts +14 -0
  20. package/dist/collapsables/unmounter/unmounter.js +43 -0
  21. package/dist/collapsables/unmounter/utils.d.ts +2 -0
  22. package/dist/collapsables/unmounter/utils.js +18 -0
  23. package/dist/country-select/country-select.js +1 -1
  24. package/dist/country-selector/connected-country-selector.js +20 -2
  25. package/dist/country-selector/country-selector-dialog/country-selector-dialog.js +1 -1
  26. package/dist/country-selector/use-countries-languages.d.ts +17 -1
  27. package/dist/country-selector/use-countries-languages.js +58 -29
  28. package/dist/delivery-time/delivery-time.d.ts +1 -1
  29. package/dist/delivery-time/delivery-time.js +20 -8
  30. package/dist/delivery-time/delivery-time.module.css.js +1 -1
  31. package/dist/exports.d.ts +15 -1
  32. package/dist/icons/glyph/glyphs-arrow-blackcaps-right-icon.js +7 -0
  33. package/dist/index.js +15 -2
  34. package/dist/intl/translation-id.d.ts +1 -1
  35. package/dist/lists/icon-list/icon-list.d.ts +10 -0
  36. package/dist/lists/icon-list/icon-list.js +11 -0
  37. package/dist/lists/icon-list/icon-list.module.css.js +3 -0
  38. package/dist/lists/menu-list/menu-list-back-button.d.ts +7 -0
  39. package/dist/lists/menu-list/menu-list-back-button.js +9 -0
  40. package/dist/lists/menu-list/menu-list-header.d.ts +7 -0
  41. package/dist/lists/menu-list/menu-list-header.js +10 -0
  42. package/dist/lists/menu-list/menu-list-item.d.ts +26 -0
  43. package/dist/lists/menu-list/menu-list-item.js +20 -0
  44. package/dist/lists/menu-list/menu-list.d.ts +14 -0
  45. package/dist/lists/menu-list/menu-list.js +13 -0
  46. package/dist/lists/menu-list/menu-list.module.css.js +3 -0
  47. package/dist/modals/signin/sign-in-dialog.js +2 -2
  48. package/dist/pages/checkout/cart-page/cart-page.js +1 -1
  49. package/dist/pages/checkout/order-confirmation-page/order-confirmation-page-content.js +1 -1
  50. package/dist/pages/checkout/payment-page/components/adyen-payment.js +1 -1
  51. package/dist/pages/checkout/payment-page/components/payment.js +9 -9
  52. package/dist/pages/checkout/payment-page/payment-page-content.js +1 -1
  53. package/dist/shared/utils/date.d.ts +1 -12
  54. package/dist/shared/utils/date.js +8 -64
  55. package/dist/styles.css +333 -6
  56. package/package.json +1 -1
  57. package/dist/lists/ul/list.d.ts +0 -10
  58. package/dist/lists/ul/list.js +0 -11
  59. package/dist/lists/ul/list.module.css.js +0 -3
  60. package/dist/shared/utils/date.test.d.ts +0 -1
@@ -9,8 +9,9 @@ export interface LinkProps {
9
9
  isDisabled?: boolean;
10
10
  onClick?: MouseEventHandler<HTMLElement>;
11
11
  onKeyUp?: (event: KeyboardEvent) => void;
12
+ role?: string;
12
13
  tabIndex?: number;
13
14
  target?: string;
14
15
  title?: string;
15
16
  }
16
- export declare function Link({ children, className, color, hasUnderline, href, id, isDisabled, onClick, onKeyUp, tabIndex, target, title, ...rest }: LinkProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function Link({ children, className, color, hasUnderline, href, id, isDisabled, onClick, onKeyUp, role, tabIndex, target, title, ...rest }: LinkProps): import("react/jsx-runtime").JSX.Element;
@@ -3,17 +3,17 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import clsx from 'clsx';
4
4
  import styles from './link.module.css.js';
5
5
 
6
- function Link({ children, className, color, hasUnderline, href, id, isDisabled, onClick, onKeyUp, tabIndex, target, title, ...rest }) {
6
+ function Link({ children, className, color, hasUnderline, href, id, isDisabled, onClick, onKeyUp, role, tabIndex, target, title, ...rest }) {
7
7
  if (href) {
8
8
  return (jsx("a", { className: clsx({
9
9
  [styles.hover]: Boolean(href || onClick),
10
10
  [styles['has-underline']]: hasUnderline,
11
- }, styles['link'], color && styles[color], className), "data-disabled": isDisabled ? true : undefined, href: href, id: id, onClick: onClick, onKeyUp: onKeyUp, tabIndex: isDisabled ? -1 : tabIndex, target: target, title: title, ...rest, children: children }));
11
+ }, styles['link'], color && styles[color], className), "data-disabled": isDisabled ? true : undefined, href: href, id: id, onClick: onClick, onKeyUp: onKeyUp, role: role, tabIndex: isDisabled ? -1 : tabIndex, target: target, title: title, ...rest, children: children }));
12
12
  }
13
13
  return (jsx("button", { className: clsx({
14
14
  [styles.hover]: Boolean(href || onClick),
15
15
  [styles['has-underline']]: hasUnderline,
16
- }, styles['link'], color && styles[color], className), "data-disabled": isDisabled ? true : undefined, disabled: isDisabled ? true : undefined, id: id, onClick: onClick, onKeyUp: onKeyUp, tabIndex: isDisabled ? -1 : tabIndex, title: title, type: "button", ...rest, children: children }));
16
+ }, styles['link'], color && styles[color], className), "data-disabled": isDisabled ? true : undefined, disabled: isDisabled ? true : undefined, id: id, onClick: onClick, onKeyUp: onKeyUp, role: role, tabIndex: isDisabled ? -1 : tabIndex, title: title, type: "button", ...rest, children: children }));
17
17
  }
18
18
 
19
19
  export { Link };
@@ -11,7 +11,7 @@ import styles from './orderline-card.module.css.js';
11
11
 
12
12
  function OrderLineCard(props) {
13
13
  const { deliveryDate, href, image, isReadonly, price, productId, sku, tags, title, } = props;
14
- return (jsxs("article", { "aria-labelledby": `title-${productId}`, className: clsx(styles['orderline-card'], isReadonly && styles.readonly), "data-disabled": isReadonly ? true : undefined, "data-product-id": sku, "data-test-selector": "orderLineListItem", id: productId, children: [isReadonly ? (jsx("div", { className: styles.title, "data-test-selector": "orderLineCardTitle", id: `title-${productId}`, children: title })) : (jsx(RouteLink, { className: styles.title, "data-test-selector": "orderLineCardTitle", href: href, id: `title-${productId}`, children: title })), tags && tags.length > 0 && (jsx("div", { className: styles.tags, children: tags.map(tag => (jsx(Tag, { children: jsx(FormattedMessage, { fallbackValue: tag, id: `tag.${tag.toLowerCase()}` }) }, tag))) })), jsx(ProductSku, { className: styles.sku, sku: sku }), jsx(Price, { className: styles.price, currencyCode: price.currencyCode, "data-test-selector": "orderLineCardPrice", originalPrice: price.originalTotalPrice, price: price.totalPrice, pricePerUnit: price.pricePerUnit, variant: "sonic" }), jsx("div", { className: styles['image-container'], role: "presentation", children: jsx(Image, { "data-test-selector": "orderLineCardImage", ...image, fit: "contain" }) }), isReadonly ? (jsx("p", { className: styles.amount, children: jsx(FormattedMessage, { id: "Amount: {0}", replacementValues: { '0': props.quantity.toString() } }) })) : (jsx("div", { className: styles['add-to-cart-button'], children: props.addToCartButton })), deliveryDate && (jsx(DeliveryTime, { className: styles.delivery, deliveryDate: deliveryDate })), !isReadonly && (jsx("div", { className: styles.remove, "data-test-selector": "orderLineCardRemove", children: props.removeButton }))] }));
14
+ return (jsxs("article", { "aria-labelledby": `title-${productId}`, className: clsx(styles['orderline-card'], isReadonly && styles.readonly), "data-disabled": isReadonly ? true : undefined, "data-product-id": sku, "data-test-selector": "orderLineListItem", id: productId, children: [isReadonly ? (jsx("div", { className: styles.title, "data-test-selector": "orderLineCardTitle", id: `title-${productId}`, children: title })) : (jsx(RouteLink, { className: styles.title, "data-test-selector": "orderLineCardTitle", href: href, id: `title-${productId}`, children: title })), tags && tags.length > 0 && (jsx("div", { className: styles.tags, children: tags.map(tag => (jsx(Tag, { children: jsx(FormattedMessage, { fallbackValue: tag, id: `tag.${tag.toLowerCase()}` }) }, tag))) })), jsx(ProductSku, { className: styles.sku, sku: sku }), jsx(Price, { className: styles.price, currencyCode: price.currencyCode, "data-test-selector": "orderLineCardPrice", originalPrice: price.originalTotalPrice, price: price.totalPrice, pricePerUnit: price.pricePerUnit, variant: "sonic" }), jsx("div", { className: styles['image-container'], role: "presentation", children: jsx(Image, { "data-test-selector": "orderLineCardImage", ...image, fit: "contain" }) }), isReadonly ? (jsx("p", { className: styles.amount, children: jsx(FormattedMessage, { id: "Amount: {0}", replacementValues: { '0': props.quantity.toString() } }) })) : (jsx("div", { className: styles['add-to-cart-button'], children: props.addToCartButton })), jsx(DeliveryTime, { className: styles.delivery, deliveryDate: deliveryDate }), !isReadonly && (jsx("div", { className: styles.remove, "data-test-selector": "orderLineCardRemove", children: props.removeButton }))] }));
15
15
  }
16
16
 
17
17
  export { OrderLineCard };
@@ -0,0 +1,2 @@
1
+ import { CascadingComponentContainerContextType } from './types';
2
+ export declare const CascadingComponentContainerContext: React.Context<CascadingComponentContainerContextType | null>;
@@ -0,0 +1,5 @@
1
+ import { createContext } from 'react';
2
+
3
+ const CascadingComponentContainerContext = createContext(null);
4
+
5
+ export { CascadingComponentContainerContext };
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from 'react';
2
+ export interface CascadingComponentContainerProps {
3
+ children: ReactNode;
4
+ onClose?: VoidFunction;
5
+ timeout?: number;
6
+ }
7
+ export declare function CascadingComponentContainerProvider({ children, onClose, timeout, }: CascadingComponentContainerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,28 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useState, useMemo, useEffect } from 'react';
3
+ import { voidFunction } from '../../shared/model/defaults.js';
4
+ import { CascadingComponentContainerContext } from './cascading-component-container-context.js';
5
+ import { useCascadingComponentContainer } from './use-cascading-component-container.js';
6
+
7
+ function CascadingComponentContainerProvider({ children, onClose, timeout, }) {
8
+ const parentContext = useCascadingComponentContainer();
9
+ const [closeChildrenFns, updateCloseChildrenFns] = useState([]);
10
+ const value = useMemo(() => ({
11
+ close: onClose || voidFunction,
12
+ closeChildCascadingComponents: () => closeChildrenFns.forEach(fn => fn()),
13
+ registerChild: onClose => updateCloseChildrenFns(fns => [...fns, onClose]),
14
+ timeout,
15
+ unregisterChild: onClose => updateCloseChildrenFns(fns => fns.filter(fn => fn !== onClose)),
16
+ }),
17
+ // eslint-disable-next-line react-hooks/exhaustive-deps
18
+ [onClose, timeout]);
19
+ useEffect(() => {
20
+ if (!parentContext || !onClose)
21
+ return;
22
+ parentContext.registerChild(onClose);
23
+ return () => parentContext.unregisterChild(onClose);
24
+ }, [onClose, parentContext]);
25
+ return (jsx(CascadingComponentContainerContext.Provider, { value: value, children: children }));
26
+ }
27
+
28
+ export { CascadingComponentContainerProvider };
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ interface CascadingComponentProps {
3
+ children: React.ReactNode;
4
+ onClose?: VoidFunction;
5
+ timeout?: number;
6
+ }
7
+ export declare function CascadingComponentContainer({ children, onClose, timeout, }: CascadingComponentProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,8 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { CascadingComponentContainerProvider } from './cascading-component-container-provider.js';
3
+
4
+ function CascadingComponentContainer({ children, onClose, timeout, }) {
5
+ return (jsx(CascadingComponentContainerProvider, { onClose: onClose, timeout: timeout, children: children }));
6
+ }
7
+
8
+ export { CascadingComponentContainer };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { RefHTMLElement } from '../unmounter/utils';
3
+ import { CascadingComponentContainerProps } from './cascading-component-container-provider';
4
+ interface CascadingComponentProps<TElement extends HTMLElement = HTMLElement, RefElement extends RefHTMLElement<TElement> = undefined> {
5
+ children: React.ReactNode;
6
+ isVisible?: boolean;
7
+ nodeRef: React.Ref<RefElement>;
8
+ onUnmounted?: () => void;
9
+ timeout?: number;
10
+ }
11
+ export declare function CascadingComponent<TElement extends HTMLElement = HTMLElement, RefElement extends RefHTMLElement<TElement> = undefined>({ children, isVisible, nodeRef, onClose, onUnmounted, timeout, }: CascadingComponentProps<TElement, RefElement> & CascadingComponentContainerProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function InternalCascadingComponent<TElement extends HTMLElement = HTMLElement, RefElement extends RefHTMLElement<TElement> = undefined>({ children, isVisible: _isVisible, nodeRef, onUnmounted, timeout: timeout, }: CascadingComponentProps<TElement, RefElement>): import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,44 @@
1
+ import { jsx, Fragment } from 'react/jsx-runtime';
2
+ import { useState, useEffect, useCallback } from 'react';
3
+ import { CSSTransition } from 'react-transition-group';
4
+ import { Unmounter } from '../unmounter/unmounter.js';
5
+ import { createAddEndListener } from '../unmounter/utils.js';
6
+ import { CascadingComponentContainerProvider } from './cascading-component-container-provider.js';
7
+ import { useCascadingComponentContainer } from './use-cascading-component-container.js';
8
+
9
+ function CascadingComponent({ children, isVisible = false, nodeRef, onClose, onUnmounted, timeout, }) {
10
+ const context = useCascadingComponentContainer();
11
+ if (!context) {
12
+ return (jsx(Fragment, { children: jsx(CascadingComponentContainerProvider, { onClose: onClose, timeout: timeout, children: jsx(InternalCascadingComponent, { isVisible: isVisible, nodeRef: nodeRef, onUnmounted: onUnmounted, timeout: timeout, children: children }) }) }));
13
+ }
14
+ return (jsx(InternalCascadingComponent, { isVisible: isVisible, nodeRef: nodeRef, onUnmounted: onUnmounted, timeout: timeout || context.timeout, children: children }));
15
+ }
16
+ function InternalCascadingComponent({ children, isVisible: _isVisible = false, nodeRef, onUnmounted, timeout: timeout, }) {
17
+ const [isVisible, setIsVisible] = useState(_isVisible);
18
+ const [unmount, setUnmount] = useState(false);
19
+ useEffect(() => {
20
+ if (_isVisible) {
21
+ setIsVisible(_isVisible);
22
+ setUnmount(false);
23
+ }
24
+ else {
25
+ setUnmount(true);
26
+ }
27
+ }, [_isVisible]);
28
+ useEffect(() => {
29
+ if (isVisible) {
30
+ setUnmount(false);
31
+ }
32
+ }, [isVisible]);
33
+ const onUnmountReady = useCallback(() => {
34
+ setIsVisible(false);
35
+ }, []);
36
+ const onClose = useCallback(() => {
37
+ setUnmount(true);
38
+ }, []);
39
+ return (jsx(CSSTransition, { mountOnEnter: true, unmountOnExit: true, ...(timeout === undefined
40
+ ? { addEndListener: createAddEndListener(nodeRef) }
41
+ : { timeout }), in: isVisible, nodeRef: nodeRef, children: jsx(Unmounter, { onUnmounted: onUnmounted, onUnmountReady: onUnmountReady, unmount: unmount, children: jsx(CascadingComponentContainerProvider, { onClose: onClose, timeout: timeout, children: children }) }) }));
42
+ }
43
+
44
+ export { CascadingComponent, InternalCascadingComponent };
@@ -0,0 +1,11 @@
1
+ export interface CascadingComponentContainerContextType {
2
+ close: VoidFunction;
3
+ closeChildCascadingComponents: VoidFunction;
4
+ registerChild: (closeFn: VoidFunction) => void;
5
+ timeout?: number;
6
+ unregisterChild: (closeFn: VoidFunction) => void;
7
+ }
8
+ export interface CascadingComponentContextType {
9
+ close: VoidFunction;
10
+ closeChildCascadingComponents: VoidFunction;
11
+ }
@@ -0,0 +1,2 @@
1
+ import { CascadingComponentContainerContextType } from './types';
2
+ export declare function useCascadingComponentContainer(): CascadingComponentContainerContextType | null;
@@ -0,0 +1,8 @@
1
+ import { useContext } from 'react';
2
+ import { CascadingComponentContainerContext } from './cascading-component-container-context.js';
3
+
4
+ function useCascadingComponentContainer() {
5
+ return useContext(CascadingComponentContainerContext);
6
+ }
7
+
8
+ export { useCascadingComponentContainer };
@@ -0,0 +1,2 @@
1
+ import { CascadingComponentContextType } from './types';
2
+ export declare function useCascadingComponent(): CascadingComponentContextType;
@@ -0,0 +1,14 @@
1
+ import { useCascadingComponentContainer } from './use-cascading-component-container.js';
2
+
3
+ function useCascadingComponent() {
4
+ const context = useCascadingComponentContainer();
5
+ if (!context) {
6
+ throw new Error('useCascadingComponent must be used within a CascadingComponentProvider');
7
+ }
8
+ return {
9
+ close: context.close,
10
+ closeChildCascadingComponents: context.closeChildCascadingComponents,
11
+ };
12
+ }
13
+
14
+ export { useCascadingComponent };
@@ -0,0 +1 @@
1
+ export declare function useHasCascadingComponentContainer(): boolean;
@@ -0,0 +1,7 @@
1
+ import { useCascadingComponentContainer } from './use-cascading-component-container.js';
2
+
3
+ function useHasCascadingComponentContainer() {
4
+ return Boolean(useCascadingComponentContainer());
5
+ }
6
+
7
+ export { useHasCascadingComponentContainer };
@@ -0,0 +1,14 @@
1
+ import { ReactNode } from 'react';
2
+ export interface UnmounterContextType {
3
+ registerChild: () => void;
4
+ unmount: boolean;
5
+ unregisterChild: () => void;
6
+ }
7
+ export declare const UnmounterContext: React.Context<UnmounterContextType | null>;
8
+ export declare function useUnmount(): UnmounterContextType | null;
9
+ export declare function Unmounter({ children: children, onUnmounted, onUnmountReady, unmount: _unmount, }: {
10
+ children: ReactNode;
11
+ onUnmountReady?: () => void;
12
+ onUnmounted?: () => void;
13
+ unmount?: boolean;
14
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,43 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, useContext, useState, useEffect, useMemo } from 'react';
3
+
4
+ const UnmounterContext = createContext(null);
5
+ function useUnmount() {
6
+ const context = useContext(UnmounterContext);
7
+ return context;
8
+ }
9
+ function Unmounter({ children: children, onUnmounted, onUnmountReady, unmount: _unmount = false, }) {
10
+ const parentContext = useUnmount();
11
+ const [hadChildren, setHadChildren] = useState(false);
12
+ const [childCount, updateChildCount] = useState(0);
13
+ const unmount = Boolean(_unmount || parentContext?.unmount);
14
+ useEffect(() => {
15
+ return () => onUnmounted?.();
16
+ }, [onUnmounted]);
17
+ useEffect(() => {
18
+ if (!parentContext)
19
+ return;
20
+ parentContext.registerChild();
21
+ return () => {
22
+ parentContext.unregisterChild();
23
+ };
24
+ }, [parentContext]);
25
+ useEffect(() => {
26
+ if (!unmount || (hadChildren && childCount > 0))
27
+ return;
28
+ onUnmountReady?.();
29
+ }, [unmount, childCount, hadChildren, onUnmountReady]);
30
+ const value = useMemo(() => {
31
+ return {
32
+ registerChild: () => {
33
+ setHadChildren(true);
34
+ updateChildCount(count => count + 1);
35
+ },
36
+ unmount,
37
+ unregisterChild: () => updateChildCount(count => count - 1),
38
+ };
39
+ }, [unmount]);
40
+ return (jsx(UnmounterContext.Provider, { value: value, children: children }));
41
+ }
42
+
43
+ export { Unmounter, UnmounterContext, useUnmount };
@@ -0,0 +1,2 @@
1
+ export type RefHTMLElement<TElement extends HTMLElement> = TElement | undefined;
2
+ export declare function createAddEndListener<TElement extends HTMLElement, RefElement extends RefHTMLElement<TElement> = undefined>(nodeRef: React.Ref<RefElement>): (doneOrNode: VoidFunction | HTMLElement, done?: VoidFunction) => void;
@@ -0,0 +1,18 @@
1
+ function createAddEndListener(nodeRef) {
2
+ return function addEndListener(doneOrNode, done) {
3
+ if (done === undefined) {
4
+ if (typeof doneOrNode === 'function') {
5
+ if (nodeRef && 'current' in nodeRef && nodeRef.current) {
6
+ return nodeRef.current.addEventListener('transitionend', doneOrNode, false);
7
+ }
8
+ throw new Error('Unsupported CSSTransition addEndListener function call');
9
+ }
10
+ }
11
+ else if (typeof doneOrNode !== 'function') {
12
+ return doneOrNode.addEventListener('transitionend', done, false);
13
+ }
14
+ throw new Error('Unsupported CSSTransition addEndListener function call');
15
+ };
16
+ }
17
+
18
+ export { createAddEndListener };
@@ -20,7 +20,7 @@ function CountrySelect({ countries, countryNameInLanguageOfCountry = false, 'dat
20
20
  }), {}),
21
21
  // eslint-disable-next-line react-hooks/exhaustive-deps
22
22
  [countries]);
23
- return (jsx(SelectField, { "data-key": selectedCountry?.id, "data-test-selector": dataTestSelector, defaultSelectedOption: defaultSelectedCountry?.id, isDisabled: isDisabled, isLoading: isLoading, isRequired: isRequired, label: t('Country'), name: name, onChange: value => onCountryChange?.(countries.find(country => country.id === value)), options: countryOptions, selectedOption: selectedCountry?.id, showLabel: showLabel, showPlaceholder: false, variant: "solid" }));
23
+ return (jsx(SelectField, { "data-key": selectedCountry?.id, "data-test-selector": dataTestSelector, defaultSelectedOption: defaultSelectedCountry?.id, isDisabled: isDisabled, isLoading: isLoading, isRequired: isRequired, label: t('Country'), name: name, onChange: value => onCountryChange?.(countries.find(country => country.id === value)), options: countryOptions, placeholder: t('Select a country'), selectedOption: selectedCountry?.id, showLabel: showLabel, variant: "solid" }));
24
24
  }
25
25
 
26
26
  export { CountrySelect };
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
3
  import { useMemo, useEffect } from 'react';
4
+ import { logger } from '../logging/logger.js';
4
5
  import { useDisclosure } from '../shared/hooks/use-disclosure.js';
5
6
  import { CountrySelectorDialog } from './country-selector-dialog/country-selector-dialog.js';
6
7
  import { CountrySelectorTrigger } from './country-selector-trigger/country-selector-trigger.js';
@@ -8,18 +9,35 @@ import { useCountriesLanguages } from './use-countries-languages.js';
8
9
 
9
10
  function ConnectedCountrySelector({ defaultCountryCode, defaultLanguageCode, onChange, showCountry = true, }) {
10
11
  const { close, isOpen, open, setIsOpen } = useDisclosure(false);
11
- const { countries, defaultCountry, defaultLanguage, isFetching, selectedCountry, selectedLanguage, updateCountryLanguage, } = useCountriesLanguages({
12
+ const { countries, defaultCountry, defaultLanguage, error, isFetching, selectedCountry, selectedLanguage, updateCountryLanguage, } = useCountriesLanguages({
12
13
  defaultCountryCode,
13
14
  defaultLanguageCode,
14
15
  });
15
16
  const isCountryLanguageSelected = useMemo(() => selectedCountry !== undefined && selectedLanguage !== undefined, [selectedCountry, selectedLanguage]);
16
17
  const isDismissable = isCountryLanguageSelected;
17
18
  useEffect(() => {
19
+ if (!isFetching) {
20
+ if (selectedCountry === undefined && selectedLanguage === undefined) {
21
+ logger.info('Opening country selector dialog because no country or language is selected');
22
+ }
23
+ else if (selectedCountry === undefined) {
24
+ logger.info('Opening country selector dialog because no country is selected');
25
+ }
26
+ else if (selectedLanguage === undefined) {
27
+ logger.info('Opening country selector dialog because no language is selected');
28
+ }
29
+ }
30
+ if (isOpen)
31
+ return;
18
32
  setIsOpen(!isFetching && !isCountryLanguageSelected);
19
33
  // eslint-disable-next-line react-hooks/exhaustive-deps
20
- }, [isFetching, isCountryLanguageSelected]);
34
+ }, [isFetching, isCountryLanguageSelected, isOpen]);
21
35
  if (isFetching)
22
36
  return null;
37
+ if (error !== undefined) {
38
+ logger.error('Error fetching countries and languages:', error);
39
+ return null;
40
+ }
23
41
  return (jsxs(Fragment, { children: [jsx(CountrySelectorTrigger, { onClick: open, selectedCountry: selectedCountry, selectedLanguage: selectedLanguage, showCountry: showCountry }), jsx(CountrySelectorDialog, { countries: countries, isDismissable: isDismissable, isOpen: isOpen, onOpenChange: open => {
24
42
  if (!isDismissable)
25
43
  return;
@@ -32,7 +32,7 @@ function CountrySelectorDialog({ countries, isDismissable, isOpen, onOpenChange,
32
32
  onSubmit({ country: currentCountry, language: currentLanguage });
33
33
  e.preventDefault();
34
34
  e.stopPropagation();
35
- }, shouldCloseOnInteractOutside: isDismissable, title: "Country Selector", children: jsxs("div", { className: styles.content, children: [jsx("svg", { className: styles.logo, height: "32", viewBox: "0 0 134 32", width: "134", xmlns: "http://www.w3.org/2000/svg", children: jsxs("g", { fill: "none", fillRule: "evenodd", children: [jsx("path", { d: "M15.83 11.533c2.42 0 4.382 1.977 4.382 4.415 0 8.794-7.1 15.95-15.83 15.95C1.963 31.897 0 29.92 0 27.481c0-2.354 1.83-4.278 4.134-4.408l.249-.007c3.797 0 6.905-3.036 7.058-6.826l.006-.293c0-2.438 1.963-4.415 4.383-4.415zM15.83 0c2.42 0 4.383 1.977 4.383 4.415 0 2.355-1.83 4.279-4.134 4.409l-.249.007c-3.8 0-6.906 3.035-7.059 6.824l-.006.293c0 2.438-1.962 4.415-4.382 4.415S0 18.386 0 15.948C0 7.154 7.101 0 15.83 0zm8.208 16.022c0 8.794 7.101 15.95 15.83 15.95s15.83-7.156 15.83-15.95S48.597.074 39.868.074s-15.83 7.154-15.83 15.948zm8.765 0c0-3.924 3.169-7.118 7.065-7.118 3.895 0 7.065 3.194 7.065 7.118 0 3.926-3.17 7.119-7.065 7.119-3.896 0-7.065-3.193-7.065-7.12zm76.888-11.276c-6.173 6.217-6.173 16.335-.002 22.553 6.173 6.217 16.215 6.217 22.388 0a4.44 4.44 0 0 0 .001-6.244 4.359 4.359 0 0 0-6.003-.184l-.194.184a7.04 7.04 0 0 1-9.994 0c-2.753-2.775-2.753-7.29.002-10.065a7.036 7.036 0 0 1 9.751-.233l.24.231a4.36 4.36 0 0 0 6.198 0 4.44 4.44 0 0 0-.001-6.245c-6.173-6.217-16.215-6.216-22.386.003zm-17.162-.174v22.863c0 2.439 1.961 4.416 4.381 4.416 2.422 0 4.384-1.977 4.384-4.416V4.572c0-2.439-1.962-4.416-4.384-4.416-2.42 0-4.38 1.977-4.38 4.416zM64.1.1c-2.42 0-4.383 1.977-4.383 4.415v23.07c0 2.438 1.962 4.415 4.382 4.415s4.383-1.977 4.383-4.415V8.93h3.933c3.895 0 7.065 3.192 7.065 7.118v11.537c0 2.438 1.962 4.415 4.382 4.415s4.383-1.977 4.383-4.415V16.048C88.244 7.254 81.143.1 72.414.1h-8.316z", fill: "#000" }), jsx("path", { d: "M47.036 16.022c0 3.99-3.209 7.222-7.168 7.222-3.96 0-7.169-3.233-7.169-7.222 0-3.989 3.21-7.222 7.169-7.222 3.96 0 7.168 3.233 7.168 7.222", fill: "#E30613" })] }) }), jsx("p", { className: styles.intro, children: jsx(FormattedMessage, { id: "Welcome to Sonic Equipment. Please choose your country and language below." }) }), jsxs("div", { className: styles.selects, children: [showCountry && (jsx(CountrySelect, { countries: countries, countryNameInLanguageOfCountry: true, "data-test-selector": "clSelector_country", onCountryChange: onCountryChange, selectedCountry: currentCountry })), jsx(Select, { "data-test-selector": "clSelector_language", label: t('Language'), onChange: value => setCurrentLanguage(currentCountry.languages.find(language => language.id === value) || currentLanguage), options: languageOptions, selectedOption: currentLanguage.id, showPlaceholder: false, variant: "solid" })] })] }) }));
35
+ }, shouldCloseOnInteractOutside: isDismissable, title: "Country Selector", children: jsxs("div", { className: styles.content, children: [jsx("svg", { className: styles.logo, height: "32", viewBox: "0 0 134 32", width: "134", xmlns: "http://www.w3.org/2000/svg", children: jsxs("g", { fill: "none", fillRule: "evenodd", children: [jsx("path", { d: "M15.83 11.533c2.42 0 4.382 1.977 4.382 4.415 0 8.794-7.1 15.95-15.83 15.95C1.963 31.897 0 29.92 0 27.481c0-2.354 1.83-4.278 4.134-4.408l.249-.007c3.797 0 6.905-3.036 7.058-6.826l.006-.293c0-2.438 1.963-4.415 4.383-4.415zM15.83 0c2.42 0 4.383 1.977 4.383 4.415 0 2.355-1.83 4.279-4.134 4.409l-.249.007c-3.8 0-6.906 3.035-7.059 6.824l-.006.293c0 2.438-1.962 4.415-4.382 4.415S0 18.386 0 15.948C0 7.154 7.101 0 15.83 0zm8.208 16.022c0 8.794 7.101 15.95 15.83 15.95s15.83-7.156 15.83-15.95S48.597.074 39.868.074s-15.83 7.154-15.83 15.948zm8.765 0c0-3.924 3.169-7.118 7.065-7.118 3.895 0 7.065 3.194 7.065 7.118 0 3.926-3.17 7.119-7.065 7.119-3.896 0-7.065-3.193-7.065-7.12zm76.888-11.276c-6.173 6.217-6.173 16.335-.002 22.553 6.173 6.217 16.215 6.217 22.388 0a4.44 4.44 0 0 0 .001-6.244 4.359 4.359 0 0 0-6.003-.184l-.194.184a7.04 7.04 0 0 1-9.994 0c-2.753-2.775-2.753-7.29.002-10.065a7.036 7.036 0 0 1 9.751-.233l.24.231a4.36 4.36 0 0 0 6.198 0 4.44 4.44 0 0 0-.001-6.245c-6.173-6.217-16.215-6.216-22.386.003zm-17.162-.174v22.863c0 2.439 1.961 4.416 4.381 4.416 2.422 0 4.384-1.977 4.384-4.416V4.572c0-2.439-1.962-4.416-4.384-4.416-2.42 0-4.38 1.977-4.38 4.416zM64.1.1c-2.42 0-4.383 1.977-4.383 4.415v23.07c0 2.438 1.962 4.415 4.382 4.415s4.383-1.977 4.383-4.415V8.93h3.933c3.895 0 7.065 3.192 7.065 7.118v11.537c0 2.438 1.962 4.415 4.382 4.415s4.383-1.977 4.383-4.415V16.048C88.244 7.254 81.143.1 72.414.1h-8.316z", fill: "#000" }), jsx("path", { d: "M47.036 16.022c0 3.99-3.209 7.222-7.168 7.222-3.96 0-7.169-3.233-7.169-7.222 0-3.989 3.21-7.222 7.169-7.222 3.96 0 7.168 3.233 7.168 7.222", fill: "#E30613" })] }) }), jsx("p", { className: styles.intro, children: jsx(FormattedMessage, { id: "Welcome to Sonic Equipment. Please choose your country and language below." }) }), jsxs("div", { className: styles.selects, children: [showCountry && (jsx(CountrySelect, { isRequired: true, countries: countries, countryNameInLanguageOfCountry: true, "data-test-selector": "clSelector_country", onCountryChange: onCountryChange, selectedCountry: currentCountry })), jsx(Select, { isRequired: true, "data-test-selector": "clSelector_language", label: t('Language'), onChange: value => setCurrentLanguage(currentCountry.languages.find(language => language.id === value) || currentLanguage), options: languageOptions, placeholder: t('Select a language'), selectedOption: currentLanguage.id, showPlaceholder: false, variant: "solid" })] })] }) }));
36
36
  }
37
37
 
38
38
  export { CountrySelectorDialog };
@@ -4,10 +4,24 @@ interface UseCountriesLanguagesArgs {
4
4
  defaultCountryCode: CountryCode;
5
5
  defaultLanguageCode: LanguageCode;
6
6
  }
7
+ interface UseCountriesLanguagesReturnTypeError {
8
+ countries: undefined;
9
+ defaultCountry: undefined;
10
+ defaultLanguage: undefined;
11
+ error: object | Error | string;
12
+ isFetching: false;
13
+ selectedCountry: undefined;
14
+ selectedLanguage: undefined;
15
+ updateCountryLanguage(args: {
16
+ country: Country;
17
+ language: Language;
18
+ }): Promise<void>;
19
+ }
7
20
  interface UseCountriesLanguagesReturnTypeFetching {
8
21
  countries: undefined;
9
22
  defaultCountry: undefined;
10
23
  defaultLanguage: undefined;
24
+ error: undefined;
11
25
  isFetching: true;
12
26
  selectedCountry: undefined;
13
27
  selectedLanguage: undefined;
@@ -20,6 +34,7 @@ interface UseCountriesLanguagesReturnTypeSelected {
20
34
  countries: Country[];
21
35
  defaultCountry: Country;
22
36
  defaultLanguage: Language;
37
+ error: undefined;
23
38
  isFetching: false;
24
39
  selectedCountry: Country;
25
40
  selectedLanguage: Language;
@@ -32,6 +47,7 @@ interface UseCountriesLanguagesReturnType {
32
47
  countries: Country[];
33
48
  defaultCountry: Country;
34
49
  defaultLanguage: Language;
50
+ error: undefined;
35
51
  isFetching: false;
36
52
  selectedCountry: Country | undefined;
37
53
  selectedLanguage: Language | undefined;
@@ -40,5 +56,5 @@ interface UseCountriesLanguagesReturnType {
40
56
  language: Language;
41
57
  }): Promise<void>;
42
58
  }
43
- export declare function useCountriesLanguages({ defaultCountryCode, defaultLanguageCode, }: UseCountriesLanguagesArgs): UseCountriesLanguagesReturnType | UseCountriesLanguagesReturnTypeFetching | UseCountriesLanguagesReturnTypeSelected;
59
+ export declare function useCountriesLanguages({ defaultCountryCode, defaultLanguageCode, }: UseCountriesLanguagesArgs): UseCountriesLanguagesReturnType | UseCountriesLanguagesReturnTypeError | UseCountriesLanguagesReturnTypeFetching | UseCountriesLanguagesReturnTypeSelected;
44
60
  export {};
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { useEffect } from 'react';
3
3
  import { config } from '../config.js';
4
+ import { logger } from '../logging/logger.js';
4
5
  import { useFetchCountriesWithLanguages } from '../shared/api/storefront/hooks/website/use-fetch-countries-with-languages.js';
5
6
  import { updateLocale } from '../shared/api/storefront/services/website-service.js';
6
7
  import { useCookie } from '../shared/hooks/use-cookie.js';
@@ -17,8 +18,8 @@ const cookieOptions = {
17
18
  function useCountriesLanguages({ defaultCountryCode, defaultLanguageCode, }) {
18
19
  const [sessionCountries, setSessionCountries] = useSessionStorage('countries-v1');
19
20
  const [currentCountryId] = useCookie('CurrentCountryId', cookieOptions);
21
+ // const [currentLanguageId] = ['be71b608-4876-420a-b1f5-adfe0093a72a'] // useCookie('CurrentLanguageId', cookieOptions)
20
22
  const [currentLanguageId] = useCookie('CurrentLanguageId', cookieOptions);
21
- const [contextLanguageCode] = useCookie('SetContextLanguageCode', cookieOptions);
22
23
  const hasSessionCountries = Boolean(sessionCountries?.length);
23
24
  const { data: apiCountries, error, isFetching, } = useFetchCountriesWithLanguages({
24
25
  enabled: !hasSessionCountries,
@@ -32,44 +33,72 @@ function useCountriesLanguages({ defaultCountryCode, defaultLanguageCode, }) {
32
33
  // eslint-disable-next-line react-hooks/exhaustive-deps
33
34
  }, [apiCountries]);
34
35
  async function updateCountryLanguage({ country, language, }) {
35
- await updateLocale({ countryId: country.id, languageId: language.id });
36
+ await updateLocale({
37
+ countryId: country.id,
38
+ languageId: language.id,
39
+ });
36
40
  }
37
- if (error)
38
- throw error;
39
- if (isFetching) {
41
+ useEffect(() => {
42
+ logger.info('CountryID from cookie', currentCountryId, countries?.find(country => country.id === currentCountryId)?.name ||
43
+ 'Unknown');
44
+ }, [countries, currentCountryId]);
45
+ useEffect(() => {
46
+ logger.info('LanguageID from cookie', currentLanguageId, countries
47
+ ?.find(country => country.id === currentCountryId)
48
+ ?.languages.find(language => language.id === currentLanguageId)
49
+ ?.description || 'Unknown');
50
+ }, [countries, currentCountryId, currentLanguageId]);
51
+ try {
52
+ if (error)
53
+ throw error;
54
+ if (isFetching) {
55
+ return {
56
+ countries: undefined,
57
+ defaultCountry: undefined,
58
+ defaultLanguage: undefined,
59
+ error: undefined,
60
+ isFetching,
61
+ selectedCountry: undefined,
62
+ selectedLanguage: undefined,
63
+ updateCountryLanguage,
64
+ };
65
+ }
66
+ if (!countries || countries.length === 0)
67
+ throw new Error('No countries found');
68
+ const defaultCountry = countries.find(country => country.abbreviation.toUpperCase() ===
69
+ defaultCountryCode.toUpperCase()) || countries[0];
70
+ if (!defaultCountry)
71
+ throw new Error(`Unable to find the default country ${defaultCountryCode}`);
72
+ const defaultLanguage = defaultCountry.languages.find(language => language.languageCode.toUpperCase() ===
73
+ defaultLanguageCode.toUpperCase()) || defaultCountry.languages[0];
74
+ if (!defaultLanguage)
75
+ throw new Error(`Unable to find the default language ${defaultLanguageCode} for country ${defaultCountry.name}`);
76
+ const selectedCountry = countries.find(country => country.id === currentCountryId);
77
+ const selectedLanguage = selectedCountry &&
78
+ selectedCountry.languages.find(language => language.id === currentLanguageId);
79
+ return {
80
+ countries,
81
+ defaultCountry,
82
+ defaultLanguage,
83
+ error: undefined,
84
+ isFetching,
85
+ selectedCountry,
86
+ selectedLanguage,
87
+ updateCountryLanguage,
88
+ };
89
+ }
90
+ catch (error) {
40
91
  return {
41
92
  countries: undefined,
42
93
  defaultCountry: undefined,
43
94
  defaultLanguage: undefined,
44
- isFetching,
95
+ error: error,
96
+ isFetching: false,
45
97
  selectedCountry: undefined,
46
98
  selectedLanguage: undefined,
47
99
  updateCountryLanguage,
48
100
  };
49
101
  }
50
- if (!countries || countries.length === 0)
51
- throw new Error('No countries found');
52
- const defaultCountry = countries.find(country => country.abbreviation.toUpperCase() === defaultCountryCode.toUpperCase()) || countries[0];
53
- if (!defaultCountry)
54
- throw new Error(`Unable to find the default country ${defaultCountryCode}`);
55
- const defaultLanguage = defaultCountry.languages.find(language => language.languageCode.toUpperCase() ===
56
- defaultLanguageCode.toUpperCase()) || defaultCountry.languages[0];
57
- if (!defaultLanguage)
58
- throw new Error(`Unable to find the default language ${defaultLanguageCode} for country ${defaultCountry.name}`);
59
- const selectedCountry = countries.find(country => country.id === currentCountryId);
60
- const selectedLanguage = selectedCountry &&
61
- (selectedCountry.languages.find(language => language.id === currentLanguageId) ||
62
- selectedCountry.languages.find(language => language.languageCode.toUpperCase() ===
63
- contextLanguageCode?.toUpperCase()));
64
- return {
65
- countries,
66
- defaultCountry,
67
- defaultLanguage,
68
- isFetching,
69
- selectedCountry,
70
- selectedLanguage,
71
- updateCountryLanguage,
72
- };
73
102
  }
74
103
 
75
104
  export { useCountriesLanguages };
@@ -1,5 +1,5 @@
1
1
  export interface DeliveryTimeProps {
2
2
  className?: string;
3
- deliveryDate: Date;
3
+ deliveryDate?: Date;
4
4
  }
5
5
  export declare function DeliveryTime({ className, deliveryDate }: DeliveryTimeProps): import("react/jsx-runtime").JSX.Element;
@@ -1,19 +1,31 @@
1
1
  "use client";
2
- import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import clsx from 'clsx';
4
4
  import { FormattedMessage } from '../intl/formatted-message.js';
5
- import { useFormattedMessage } from '../intl/use-formatted-message.js';
6
- import { getDateUnitObject } from '../shared/utils/date.js';
7
5
  import { InfoIconTooltip } from '../info-icon-tooltip/info-icon-tooltip.js';
6
+ import { useFormattedMessage } from '../intl/use-formatted-message.js';
7
+ import { useIntl } from '../intl/use-intl.js';
8
+ import { formatDateToLocaleString } from '../shared/utils/date.js';
8
9
  import styles from './delivery-time.module.css.js';
9
10
 
10
11
  function DeliveryTime({ className, deliveryDate }) {
12
+ const { cultureCode } = useIntl();
11
13
  const t = useFormattedMessage();
12
- const { number, unit } = getDateUnitObject(deliveryDate);
13
- return (jsxs("div", { className: clsx(styles['delivery-time'], className), children: [jsx("p", { "data-test-selector": "deliveryTime", children: jsx(FormattedMessage, { id: "Delivery expected in {0} {1}", replacementValues: {
14
- '0': number.toString(),
15
- '1': t.pluralize('unit', unit, number),
16
- } }) }), jsx(InfoIconTooltip, { variant: "stroke", children: jsx(FormattedMessage, { id: "The expected delivery is an indication based on the product availability and the shipping location." }) })] }));
14
+ if (!deliveryDate) {
15
+ return (jsx("div", { className: clsx(styles['delivery-time'], className), children: jsx("p", {
16
+ // eslint-disable-next-line @eslint-react/dom/no-dangerously-set-innerhtml
17
+ dangerouslySetInnerHTML: {
18
+ __html: t('Availability unknown, please contact customer support for lead time or alternatives.'),
19
+ }, "data-test-selector": "deliveryTime_unknown" }) }));
20
+ }
21
+ const localeDate = formatDateToLocaleString(deliveryDate, cultureCode, {
22
+ day: 'numeric',
23
+ month: 'short',
24
+ year: 'numeric',
25
+ });
26
+ return (jsxs("div", { className: clsx(styles['delivery-time'], className), children: [jsx("p", { "data-test-selector": "deliveryTime", children: jsx(FormattedMessage, { id: "Delivery expected on {0}", replacementValues: {
27
+ '0': localeDate,
28
+ } }) }), jsx(InfoIconTooltip, { className: styles['info'], variant: "stroke", children: jsx(FormattedMessage, { id: "The expected delivery is an indication based on the product availability and the shipping location." }) })] }));
17
29
  }
18
30
 
19
31
  export { DeliveryTime };
@@ -1,3 +1,3 @@
1
- var styles = {"delivery-time":"delivery-time-module-fEWEl"};
1
+ var styles = {"delivery-time":"delivery-time-module-fEWEl","info":"delivery-time-module-Ta7xm"};
2
2
 
3
3
  export { styles as default };