@sonic-equipment/ui 187.0.0 → 189.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 (108) hide show
  1. package/dist/cards/data-card/data-card.js +1 -1
  2. package/dist/country-selector/connected-country-selector.js +0 -11
  3. package/dist/country-selector/use-countries-languages.js +0 -11
  4. package/dist/delivery-time/delivery-time.js +3 -4
  5. package/dist/exports.d.ts +13 -3
  6. package/dist/footer/connected-footer.js +8 -1
  7. package/dist/footer/footer.d.ts +5 -10
  8. package/dist/footer/footer.js +23 -18
  9. package/dist/forms/fields/select-field/select-field.js +1 -1
  10. package/dist/forms/layout/form/form.js +1 -1
  11. package/dist/header/connected-header.js +9 -1
  12. package/dist/header/drawers/desktop-navigation-drawer.d.ts +3 -3
  13. package/dist/header/drawers/desktop-navigation-drawer.js +2 -4
  14. package/dist/header/drawers/mobile-navigation-drawer.d.ts +3 -3
  15. package/dist/header/drawers/mobile-navigation-drawer.js +2 -2
  16. package/dist/header/header.d.ts +3 -3
  17. package/dist/header/header.js +15 -9
  18. package/dist/header/link-list/navigation-link-list.d.ts +5 -5
  19. package/dist/header/link-list/navigation-link-list.js +20 -5
  20. package/dist/index.js +14 -4
  21. package/dist/intl/formatted-date.d.ts +4 -0
  22. package/dist/intl/formatted-date.js +8 -0
  23. package/dist/intl/translation-id.d.ts +1 -1
  24. package/dist/intl/use-formatted-date.d.ts +6 -0
  25. package/dist/intl/use-formatted-date.js +19 -0
  26. package/dist/lists/menu-list/menu-list-header.d.ts +1 -1
  27. package/dist/lists/menu-list/menu-list-item.d.ts +2 -2
  28. package/dist/lists/menu-list/menu-list-item.js +3 -1
  29. package/dist/lists/menu-list/menu-list-provider.d.ts +11 -0
  30. package/dist/lists/menu-list/menu-list-provider.js +30 -0
  31. package/dist/lists/menu-list/menu-list.d.ts +8 -5
  32. package/dist/lists/menu-list/menu-list.js +6 -5
  33. package/dist/lists/menu-list/menu-list.module.css.js +1 -1
  34. package/dist/lists/menu-list/use-menu-list-item.d.ts +5 -0
  35. package/dist/lists/menu-list/use-menu-list-item.js +12 -0
  36. package/dist/lists/menu-list/use-menu-list.d.ts +1 -0
  37. package/dist/lists/menu-list/use-menu-list.js +11 -0
  38. package/dist/loading/dynamic-loading-overlay.d.ts +5 -0
  39. package/dist/loading/dynamic-loading-overlay.js +26 -0
  40. package/dist/loading/dynamic-loading-overlay.module.css.js +3 -0
  41. package/dist/media/image-lightbox/image-lightbox.js +12 -2
  42. package/dist/media/zoom-image/zoom-image.js +6 -1
  43. package/dist/navigation/mobile-navigation/mobile-navigation.d.ts +3 -3
  44. package/dist/navigation/mobile-navigation/mobile-navigation.js +2 -2
  45. package/dist/navigation/panel-navigation/panel-navigation.d.ts +5 -7
  46. package/dist/navigation/panel-navigation/panel-navigation.js +11 -14
  47. package/dist/pages/checkout/cart-page/cart-page.js +1 -1
  48. package/dist/pages/checkout/order-confirmation-page/order-confirmation-page-content.js +2 -4
  49. package/dist/pages/error-page/error-page.js +2 -7
  50. package/dist/pages/my-sonic/actions/change-password/connected-change-password-dialog.js +0 -2
  51. package/dist/pages/my-sonic/navigation/my-sonic-desktop-navigation.js +1 -1
  52. package/dist/pages/my-sonic/pages/order-history/order-history.d.ts +1 -0
  53. package/dist/pages/my-sonic/pages/order-history/order-history.js +166 -0
  54. package/dist/pages/my-sonic/pages/order-history/order-history.module.css.js +3 -0
  55. package/dist/pages/product/product-listing-page/product-listing.js +1 -1
  56. package/dist/pages/product/search-result-page/search-results-page.js +1 -1
  57. package/dist/shared/api/bff/hooks/use-fetch-navigation-links.d.ts +1 -1
  58. package/dist/shared/api/bff/model/bff.model.d.ts +22 -14
  59. package/dist/shared/api/bff/model/bff.model.js +11 -0
  60. package/dist/shared/api/bff/services/bff-service.d.ts +2 -2
  61. package/dist/shared/api/bff/services/bff-service.js +1 -1
  62. package/dist/shared/api/storefront/hooks/orders/use-fetch-orders.d.ts +10 -0
  63. package/dist/shared/api/storefront/hooks/orders/use-fetch-orders.js +13 -0
  64. package/dist/shared/api/storefront/model/storefront.model.d.ts +13 -1
  65. package/dist/shared/api/storefront/model/storefront.model.js +28 -1
  66. package/dist/shared/api/storefront/services/order-service.d.ts +12 -0
  67. package/dist/shared/api/storefront/services/order-service.js +29 -0
  68. package/dist/shared/data/navigation.d.ts +2 -0
  69. package/dist/shared/data/navigation.js +2605 -0
  70. package/dist/shared/hooks/use-intersection-observer.d.ts +3 -1
  71. package/dist/shared/hooks/use-intersection-observer.js +3 -2
  72. package/dist/shared/utils/date.d.ts +4 -1
  73. package/dist/shared/utils/date.js +6 -5
  74. package/dist/shared/utils/price.d.ts +1 -1
  75. package/dist/shared/{hooks/use-scroll-to.d.ts → utils/scrolling.d.ts} +1 -1
  76. package/dist/shared/utils/scrolling.js +16 -0
  77. package/dist/shared/utils/uuid.d.ts +2 -1
  78. package/dist/styles.css +896 -634
  79. package/dist/table/data-table.d.ts +37 -10
  80. package/dist/table/data-table.js +72 -17
  81. package/dist/table/data-table.module.css.js +1 -1
  82. package/dist/table/elements/switch-sort-direction.d.ts +2 -0
  83. package/dist/table/elements/switch-sort-direction.js +11 -0
  84. package/dist/table/elements/table-context.d.ts +6 -5
  85. package/dist/table/elements/table-context.js +1 -16
  86. package/dist/table/elements/table-provider.d.ts +6 -5
  87. package/dist/table/elements/table-provider.js +23 -17
  88. package/dist/table/elements/table-row-context.d.ts +2 -2
  89. package/dist/table/elements/table-row-provider.js +4 -4
  90. package/dist/table/elements/table-sort-button.d.ts +8 -7
  91. package/dist/table/elements/table-sort-button.js +5 -4
  92. package/dist/table/elements/table.d.ts +1 -1
  93. package/dist/table/elements/table.js +2 -2
  94. package/dist/table/elements/table.module.css.js +1 -1
  95. package/dist/table/elements/td.d.ts +1 -1
  96. package/dist/table/elements/th.d.ts +1 -7
  97. package/dist/table/elements/th.js +2 -3
  98. package/dist/table/elements/tr.d.ts +2 -8
  99. package/dist/table/elements/tr.js +1 -3
  100. package/dist/table/elements/types.d.ts +36 -0
  101. package/dist/table/elements/use-table.js +1 -3
  102. package/dist/table/elements/use-td.d.ts +1 -1
  103. package/dist/table/elements/use-th.d.ts +2 -2
  104. package/package.json +1 -1
  105. package/dist/shared/hooks/use-scroll-to.js +0 -19
  106. package/dist/table/elements/table-column-properties.d.ts +0 -10
  107. package/dist/table/elements/use-tr.d.ts +0 -2
  108. package/dist/table/elements/use-tr.js +0 -16
@@ -18,7 +18,7 @@ function renderValue(rendering, key, value) {
18
18
  const renderedValue = rendering?.value
19
19
  ? rendering.value({ key, value })
20
20
  : value !== undefined && value !== null
21
- ? String(value)
21
+ ? value
22
22
  : null;
23
23
  if (renderedValue === null ||
24
24
  renderedValue === undefined ||
@@ -16,17 +16,6 @@ function ConnectedCountrySelector({ defaultCountryCode, defaultLanguageCode, onC
16
16
  const isCountryLanguageSelected = useMemo(() => selectedCountry !== undefined && selectedLanguage !== undefined, [selectedCountry, selectedLanguage]);
17
17
  const isDismissable = isCountryLanguageSelected;
18
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
19
  if (isOpen)
31
20
  return;
32
21
  setIsOpen(!isFetching && !isCountryLanguageSelected);
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
  import { useEffect } from 'react';
3
3
  import { config } from '../config.js';
4
- import { logger } from '../logging/logger.js';
5
4
  import { useFetchCountriesWithLanguages } from '../shared/api/storefront/hooks/website/use-fetch-countries-with-languages.js';
6
5
  import { updateLocale } from '../shared/api/storefront/services/website-service.js';
7
6
  import { useCookie } from '../shared/hooks/use-cookie.js';
@@ -38,16 +37,6 @@ function useCountriesLanguages({ defaultCountryCode, defaultLanguageCode, }) {
38
37
  languageId: language.id,
39
38
  });
40
39
  }
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
40
  try {
52
41
  if (error)
53
42
  throw error;
@@ -3,14 +3,13 @@ import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import clsx from 'clsx';
4
4
  import { FormattedMessage } from '../intl/formatted-message.js';
5
5
  import { InfoIconTooltip } from '../info-icon-tooltip/info-icon-tooltip.js';
6
+ import { useFormattedDate } from '../intl/use-formatted-date.js';
6
7
  import { useFormattedMessage } from '../intl/use-formatted-message.js';
7
- import { useIntl } from '../intl/use-intl.js';
8
- import { formatDateToLocaleString } from '../shared/utils/date.js';
9
8
  import styles from './delivery-time.module.css.js';
10
9
 
11
10
  function DeliveryTime({ className, deliveryDate }) {
12
- const { cultureCode } = useIntl();
13
11
  const t = useFormattedMessage();
12
+ const formatDate = useFormattedDate();
14
13
  if (deliveryDate === undefined)
15
14
  return null;
16
15
  if (deliveryDate === null) {
@@ -20,7 +19,7 @@ function DeliveryTime({ className, deliveryDate }) {
20
19
  __html: t('Availability unknown, please contact customer support for lead time or alternatives.'),
21
20
  }, "data-test-selector": "deliveryTime_unknown" }) }));
22
21
  }
23
- const localeDate = formatDateToLocaleString(deliveryDate, cultureCode, {
22
+ const localeDate = formatDate(deliveryDate, {
24
23
  day: 'numeric',
25
24
  month: 'short',
26
25
  year: 'numeric',
package/dist/exports.d.ts CHANGED
@@ -149,6 +149,7 @@ export * from './header/header-layout/header-layout';
149
149
  export * from './header/link-list/navigation-link-list';
150
150
  export * from './header/sonic-logo/sonic-logo';
151
151
  export * from './info-icon-tooltip/info-icon-tooltip';
152
+ export * from './intl/formatted-date';
152
153
  export * from './intl/formatted-message';
153
154
  export * from './intl/intl-context';
154
155
  export * from './intl/intl-provider';
@@ -158,6 +159,7 @@ export * from './intl/types';
158
159
  export * from './intl/use-country-code';
159
160
  export * from './intl/use-culture-code';
160
161
  export * from './intl/use-currency-code';
162
+ export * from './intl/use-formatted-date';
161
163
  export * from './intl/use-formatted-message';
162
164
  export * from './intl/use-intl';
163
165
  export * from './intl/use-language-code';
@@ -170,11 +172,15 @@ export * from './lists/menu-list/menu-list';
170
172
  export * from './lists/menu-list/menu-list-back-button';
171
173
  export * from './lists/menu-list/menu-list-header';
172
174
  export * from './lists/menu-list/menu-list-item';
175
+ export * from './lists/menu-list/menu-list-provider';
176
+ export * from './lists/menu-list/use-menu-list';
177
+ export * from './lists/menu-list/use-menu-list-item';
173
178
  export * from './lists/orderline-list/orderline-list';
174
179
  export * from './lists/product-overview-grid/product-overview-grid';
175
180
  export * from './lists/widget-grid/widget';
176
181
  export * from './lists/widget-grid/widget-grid';
177
182
  export * from './loading/blank-page-spacer';
183
+ export * from './loading/dynamic-loading-overlay';
178
184
  export * from './loading/loading-overlay';
179
185
  export * from './loading/progress-circle';
180
186
  export * from './logging/logger';
@@ -255,6 +261,7 @@ export * from './pages/my-sonic/navigation/connected-my-sonic-navigation';
255
261
  export * from './pages/my-sonic/navigation/my-sonic-desktop-navigation';
256
262
  export * from './pages/my-sonic/navigation/my-sonic-mobile-navigation';
257
263
  export * from './pages/my-sonic/navigation/my-sonic-navigation-items';
264
+ export * from './pages/my-sonic/pages/order-history/order-history';
258
265
  export * from './pages/my-sonic/widgets/components/address-data-card';
259
266
  export * from './pages/my-sonic/widgets/connected-bill-to-address-widget';
260
267
  export * from './pages/my-sonic/widgets/connected-customer-information-widget';
@@ -324,6 +331,7 @@ export * from './shared/api/storefront/hooks/customer/use-fetch-fulfillment-meth
324
331
  export * from './shared/api/storefront/hooks/customer/use-fetch-fulfillment-methods-for-current-cart';
325
332
  export * from './shared/api/storefront/hooks/customer/use-fetch-ship-to-addresses';
326
333
  export * from './shared/api/storefront/hooks/customer/use-patch-bill-to-address';
334
+ export * from './shared/api/storefront/hooks/orders/use-fetch-orders';
327
335
  export * from './shared/api/storefront/hooks/payment/use-create-adyen-session';
328
336
  export * from './shared/api/storefront/hooks/payment/use-fetch-adyen-config';
329
337
  export * from './shared/api/storefront/hooks/payment/use-invalidate-adyen';
@@ -345,6 +353,7 @@ export * from './shared/api/storefront/services/authentication-service';
345
353
  export * from './shared/api/storefront/services/cart-service';
346
354
  export * from './shared/api/storefront/services/customer-service';
347
355
  export * from './shared/api/storefront/services/finance-service';
356
+ export * from './shared/api/storefront/services/order-service';
348
357
  export * from './shared/api/storefront/services/payment-service';
349
358
  export * from './shared/api/storefront/services/product-service';
350
359
  export * from './shared/api/storefront/services/translation-service';
@@ -352,6 +361,7 @@ export * from './shared/api/storefront/services/website-service';
352
361
  export * from './shared/api/storefront/services/wishlist-service';
353
362
  export * from './shared/data/cart.data';
354
363
  export * from './shared/data/countries-languages.data';
364
+ export * from './shared/data/navigation';
355
365
  export * from './shared/feature-flags/use-feature-flags';
356
366
  export * from './shared/fetch/request';
357
367
  export * from './shared/ga/data-layer';
@@ -372,7 +382,6 @@ export * from './shared/hooks/use-mutation-observer';
372
382
  export * from './shared/hooks/use-resize-observer';
373
383
  export * from './shared/hooks/use-script';
374
384
  export * from './shared/hooks/use-scroll-lock';
375
- export * from './shared/hooks/use-scroll-to';
376
385
  export * from './shared/hooks/use-session-storage';
377
386
  export * from './shared/hooks/use-watch-css-property';
378
387
  export * from './shared/model/account';
@@ -411,6 +420,7 @@ export * from './shared/utils/price';
411
420
  export * from './shared/utils/promise';
412
421
  export * from './shared/utils/random';
413
422
  export * from './shared/utils/refs';
423
+ export * from './shared/utils/scrolling';
414
424
  export * from './shared/utils/string';
415
425
  export * from './shared/utils/time';
416
426
  export * from './shared/utils/types';
@@ -422,8 +432,8 @@ export * from './sidebar/types';
422
432
  export * from './sidebar/use-sidebar';
423
433
  export * from './table/data-table';
424
434
  export * from './table/elements/col';
435
+ export * from './table/elements/switch-sort-direction';
425
436
  export * from './table/elements/table';
426
- export * from './table/elements/table-column-properties';
427
437
  export * from './table/elements/table-context';
428
438
  export * from './table/elements/table-provider';
429
439
  export * from './table/elements/table-row-context';
@@ -432,11 +442,11 @@ export * from './table/elements/table-sort-button';
432
442
  export * from './table/elements/td';
433
443
  export * from './table/elements/th';
434
444
  export * from './table/elements/tr';
445
+ export * from './table/elements/types';
435
446
  export * from './table/elements/use-table';
436
447
  export * from './table/elements/use-table-row';
437
448
  export * from './table/elements/use-td';
438
449
  export * from './table/elements/use-th';
439
- export * from './table/elements/use-tr';
440
450
  export * from './text/highlight-text/highlight-text';
441
451
  export * from './text/truncated/truncated';
442
452
  export * from './toast/toast';
@@ -1,14 +1,21 @@
1
1
  "use client";
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { ConnectedCountrySelector } from '../country-selector/connected-country-selector.js';
4
+ import { FormattedMessage } from '../intl/formatted-message.js';
4
5
  import { useCultureCode } from '../intl/use-culture-code.js';
5
6
  import { useFetchNavigationLinks } from '../shared/api/bff/hooks/use-fetch-navigation-links.js';
7
+ import { isNavigationSection } from '../shared/api/bff/model/bff.model.js';
6
8
  import { Footer } from './footer.js';
7
9
 
8
10
  function ConnectedFooter({ className, onCountryLanguageChange, source, }) {
9
11
  const cultureCode = useCultureCode();
10
12
  const { data } = useFetchNavigationLinks({ cultureCode, source });
11
- return (jsx(Footer, { bottomLinks: data?.footer.bottomLinks || [], className: className, copyright: data?.footer.copyright || '', countrySelector: jsx(ConnectedCountrySelector, { defaultCountryCode: "NL", defaultLanguageCode: "EN", onChange: onCountryLanguageChange }), linkBlocks: data?.footer.linkBlocks || [] }));
13
+ if (data && data.key !== 'navigation')
14
+ throw new Error('Invalid navigation data');
15
+ const footerNavigationSection = data?.items
16
+ .filter(isNavigationSection)
17
+ .find(item => item.key === 'footer');
18
+ return (jsx(Footer, { className: className, copyright: jsx(FormattedMessage, { id: "Copyright \u00A9 Sonic Equipment B.V." }), countrySelector: jsx(ConnectedCountrySelector, { defaultCountryCode: "NL", defaultLanguageCode: "EN", onChange: onCountryLanguageChange }), footerNavigationSection: footerNavigationSection }));
12
19
  }
13
20
 
14
21
  export { ConnectedFooter };
@@ -1,14 +1,9 @@
1
1
  import { ReactNode } from 'react';
2
- import { NavigationLink } from '../shared/api/bff/model/bff.model';
3
- export interface FooterBottomSectionProps {
4
- bottomLinks: NavigationLink[];
2
+ import { NavigationSection } from '../shared/api/bff/model/bff.model';
3
+ export interface FooterProps {
5
4
  className?: string;
6
- copyright: string;
5
+ copyright: ReactNode;
7
6
  countrySelector: ReactNode;
7
+ footerNavigationSection: NavigationSection | undefined;
8
8
  }
9
- export interface FooterLinkBlockProps {
10
- linkBlocks: NavigationLink[];
11
- }
12
- export interface FooterProps extends FooterBottomSectionProps, FooterLinkBlockProps {
13
- }
14
- export declare function Footer({ bottomLinks, className, copyright, countrySelector, linkBlocks, }: FooterProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function Footer({ className, copyright, countrySelector, footerNavigationSection, }: FooterProps): import("react/jsx-runtime").JSX.Element;
@@ -4,33 +4,38 @@ import clsx from 'clsx';
4
4
  import { Link } from '../buttons/link/link.js';
5
5
  import { Accordion } from '../collapsables/accordion/accordion.js';
6
6
  import { AccordionItem } from '../collapsables/accordion/accordion-item.js';
7
- import { logger } from '../logging/logger.js';
7
+ import { isNavigationSection, isNavigationLinkItem, isNavigationLink } from '../shared/api/bff/model/bff.model.js';
8
8
  import { useIsBreakpoint } from '../shared/hooks/use-is-breakpoint.js';
9
9
  import styles from './footer.module.css.js';
10
10
 
11
- function BottomSection({ bottomLinks, copyright, countrySelector, }) {
12
- return (jsxs("section", { className: styles['bottom-section'], children: [copyright && jsx("p", { className: styles.copyright, children: copyright }), bottomLinks.length > 0 && (jsx("ul", { className: styles['bottom-links'], role: "navigation", children: bottomLinks.map(link => (jsx("li", { children: jsx(Link, { href: link.url, target: link.openInNewTab ? '_blank' : undefined, children: link.title }) }, link.key))) })), countrySelector && (jsx("div", { className: styles['country-selector'], children: countrySelector }))] }));
11
+ function BottomSection({ copyright, countrySelector, footerBottomSection, }) {
12
+ if (!footerBottomSection)
13
+ return null;
14
+ const links = footerBottomSection.items.filter(isNavigationLink);
15
+ return (jsxs("section", { className: styles['bottom-section'], children: [copyright && jsx("p", { className: styles.copyright, children: copyright }), footerBottomSection.items.length > 0 && (jsx("ul", { className: styles['bottom-links'], children: links.map(link => (jsx(Link, { className: styles['bottom-link'], href: link.href || undefined, target: link.openInNewTab ? '_blank' : undefined, children: link.label }, link.key))) })), countrySelector && (jsx("div", { className: styles['country-selector'], children: countrySelector }))] }));
13
16
  }
14
- function LinkSection({ link }) {
17
+ function LinkSection({ linkGroup }) {
15
18
  const isXl = useIsBreakpoint('xl');
16
- return (jsx("section", { className: styles['link-block'], children: jsx(Accordion, { color: "white", hasLineSeparator: false, size: "lg", children: jsx(AccordionItem, { allowCollapse: !isXl, id: `link-block-${link.key}`, initialIsOpen: isXl, title: link.title, children: jsx("ul", { className: styles['list'], children: link.links?.map(link => (jsx("li", { children: jsx(FooterLink, { link: link }) }, link.key))) }) }) }) }, link.key));
19
+ return (jsx("section", { className: styles['link-block'], children: jsx(Accordion, { color: "white", hasLineSeparator: false, size: "lg", children: jsx(AccordionItem, { allowCollapse: !isXl, id: `link-block-${linkGroup.key}`, initialIsOpen: isXl, title: linkGroup.header || linkGroup.label, children: jsx("ul", { className: styles['list'], children: linkGroup.items.filter(isNavigationLink).map(link => (jsx("li", { children: jsx(FooterLink, { link: link }) }, link.key))) }) }) }) }, linkGroup.key));
17
20
  }
18
21
  function FooterLink({ link }) {
19
- const type = link.type;
20
- if (type === 'link' || type === 'category') {
21
- return (jsx(Link, { href: link.url, target: link.openInNewTab ? '_blank' : undefined, children: link.title }));
22
- }
23
- if (type === 'section') {
24
- return jsx(LinkSection, { link: link });
25
- }
26
- logger.warn(`Unknown link type '${type}' in footer with key '${link.key}'`);
27
- return null;
22
+ return (jsx(Link, { href: link.href || undefined, target: link.openInNewTab ? '_blank' : undefined, children: link.label }));
28
23
  }
29
- function FooterLinkBlock({ linkBlocks }) {
30
- return (jsx("nav", { className: styles['main-links'], children: linkBlocks.map(link => (jsx(FooterLink, { link: link }, link.key))) }));
24
+ function FooterLinkBlock({ footerLinkBlockSection }) {
25
+ if (!footerLinkBlockSection)
26
+ return null;
27
+ return (jsx("nav", { className: styles['main-links'], children: footerLinkBlockSection.items
28
+ .filter(isNavigationLinkItem)
29
+ .map(linkItem => isNavigationLink(linkItem) ? (jsx(FooterLink, { link: linkItem }, linkItem.key)) : (jsx(LinkSection, { linkGroup: linkItem }, linkItem.key))) }));
31
30
  }
32
- function Footer({ bottomLinks, className, copyright, countrySelector, linkBlocks, }) {
33
- return (jsxs("footer", { className: clsx(styles.footer, className), children: [jsx(FooterLinkBlock, { linkBlocks: linkBlocks }), jsx(BottomSection, { bottomLinks: bottomLinks, copyright: copyright, countrySelector: countrySelector })] }));
31
+ function Footer({ className, copyright, countrySelector, footerNavigationSection, }) {
32
+ const footerLinkBlockSection = footerNavigationSection?.items
33
+ .filter(isNavigationSection)
34
+ .find(section => section.key === 'footer-link-block');
35
+ const footerBottomSection = footerNavigationSection?.items
36
+ .filter(isNavigationSection)
37
+ .find(section => section.key === 'footer-bottom');
38
+ return (jsxs("footer", { className: clsx(styles.footer, className), children: [jsx(FooterLinkBlock, { footerLinkBlockSection: footerLinkBlockSection }), jsx(BottomSection, { copyright: copyright, countrySelector: countrySelector, footerBottomSection: footerBottomSection })] }));
34
39
  }
35
40
 
36
41
  export { Footer };
@@ -26,7 +26,7 @@ function SelectField({ className, 'data-test-selector': dataTestSelector, defaul
26
26
  [styles['loading']]: isLoading,
27
27
  }, className), "data-test-selector": dataTestSelector, defaultSelectedKey: defaultSelectedOption === undefined
28
28
  ? undefined
29
- : String(defaultSelectedOption), isDisabled: isDisabled, isInvalid: isInvalid, isRequired: isRequired, name: name, onSelectionChange: key => handleChange(key), placeholder: placeholder || label, selectedKey: selectedOption === undefined ? undefined : String(selectedOption), children: jsx(FormFieldLayout, { errorSlot: jsx(FieldError, {}), infoSlot: info && jsx(InfoIconTooltip, { children: info }), labelSlot: showLabel && jsx(Label, { isRequired: isRequired, children: label }), children: jsxs(Fragment, { children: [jsxs(Button, { className: styles.button, children: [jsx(SelectValue, { className: styles.value, "data-test-selector": "value" }), isLoading ? (jsx(ProgressCircle, { "aria-hidden": "true", className: styles.icon, variant: "gray" })) : (jsx(GlyphsChevronsSlimDownIcon, { "aria-hidden": "true", className: styles.icon }))] }), jsx(Popover, { className: clsx(styles.popover, styles[variant]), placement: "bottom left", children: jsx(ListBox, { className: styles.listbox, "data-test-selector": dataTestSelector ? `${dataTestSelector}_options` : undefined, children: jsxs(ListBoxSection, { children: [showPlaceholder && (jsx(Header, { className: styles.header, children: placeholder || label })), Object.entries(options).map(([key, value]) => (jsxs(ListBoxItem, { "aria-label": value, className: styles.item, id: key, textValue: value, children: [selectedKey === key && (jsx("span", { slot: "description", children: jsx(StrokeCheckmarkIcon, { className: styles.check }) })), jsx("span", { slot: "label", children: value })] }, key)))] }) }) })] }) }) }));
29
+ : String(defaultSelectedOption), isDisabled: isDisabled, isInvalid: isInvalid, isRequired: isRequired, name: name, onSelectionChange: key => handleChange(key), placeholder: placeholder || label, selectedKey: selectedOption === undefined ? undefined : String(selectedOption), children: jsx(FormFieldLayout, { errorSlot: jsx(FieldError, {}), infoSlot: info && jsx(InfoIconTooltip, { children: info }), labelSlot: showLabel && jsx(Label, { isRequired: isRequired, children: label }), children: jsxs(Fragment, { children: [jsxs(Button, { className: styles.button, children: [jsx(SelectValue, { className: styles.value, "data-test-selector": "value" }), isLoading ? (jsx(ProgressCircle, { "aria-hidden": "true", className: styles.loading, variant: "gray" })) : (jsx(GlyphsChevronsSlimDownIcon, { "aria-hidden": "true", className: styles.icon }))] }), jsx(Popover, { className: clsx(styles.popover, styles[variant]), placement: "bottom left", children: jsx(ListBox, { className: styles.listbox, "data-test-selector": dataTestSelector ? `${dataTestSelector}_options` : undefined, children: jsxs(ListBoxSection, { children: [showPlaceholder && (jsx(Header, { className: styles.header, children: placeholder || label })), Object.entries(options).map(([key, value]) => (jsxs(ListBoxItem, { "aria-label": value, className: styles.item, id: key, textValue: value, children: [selectedKey === key && (jsx("span", { slot: "description", children: jsx(StrokeCheckmarkIcon, { className: styles.check }) })), jsx("span", { slot: "label", children: value })] }, key)))] }) }) })] }) }) }));
30
30
  }
31
31
 
32
32
  export { SelectField };
@@ -4,7 +4,7 @@ import { useEffect } from 'react';
4
4
  import { Form as Form$1 } from 'react-aria-components';
5
5
  import clsx from 'clsx';
6
6
  import { Message } from '../../../message/message.js';
7
- import { scrollToTop } from '../../../shared/hooks/use-scroll-to.js';
7
+ import { scrollToTop } from '../../../shared/utils/scrolling.js';
8
8
  import styles from './form.module.css.js';
9
9
 
10
10
  function Form({ autoComplete = false, children, className, errorMessage, footer, header, onSubmit, title, validationErrors, }) {
@@ -2,12 +2,20 @@
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { useCultureCode } from '../intl/use-culture-code.js';
4
4
  import { useFetchNavigationLinks } from '../shared/api/bff/hooks/use-fetch-navigation-links.js';
5
+ import { isNavigationSection } from '../shared/api/bff/model/bff.model.js';
5
6
  import { Header } from './header.js';
6
7
 
7
8
  function ConnectedHeader({ className, source, }) {
8
9
  const cultureCode = useCultureCode();
9
10
  const { data } = useFetchNavigationLinks({ cultureCode, source });
10
- return (jsx(Header, { className: className, links: data?.header ?? [], sticky: true }));
11
+ if (data && data.key !== 'navigation')
12
+ throw new Error('Invalid navigation data');
13
+ const headerNavigationSection = data?.items
14
+ .filter(isNavigationSection)
15
+ .find(item => item.key === 'header');
16
+ if (data && !headerNavigationSection)
17
+ throw new Error('Header section not found in navigation data');
18
+ return (jsx(Header, { className: className, headerNavigationSection: headerNavigationSection, sticky: true }));
11
19
  }
12
20
 
13
21
  export { ConnectedHeader };
@@ -1,8 +1,8 @@
1
- import { NavigationLink } from '../../shared/api/bff/model/bff.model';
1
+ import { NavigationLinkGroup } from '../../shared/api/bff/model/bff.model';
2
2
  export interface DesktopNavigationDrawerProps {
3
3
  groupId?: string;
4
4
  instanceId?: string;
5
+ linkGroup?: NavigationLinkGroup;
5
6
  onClosed?: () => void;
6
- submenu?: NavigationLink;
7
7
  }
8
- export declare function DesktopNavigationDrawer({ groupId, instanceId, onClosed, submenu, }: DesktopNavigationDrawerProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function DesktopNavigationDrawer({ groupId, instanceId, linkGroup, onClosed, }: DesktopNavigationDrawerProps): import("react/jsx-runtime").JSX.Element;
@@ -4,10 +4,8 @@ import { Drawer } from '../../drawer/drawer.js';
4
4
  import { PanelNavigation } from '../../navigation/panel-navigation/panel-navigation.js';
5
5
  import styles from './desktop-navigation-drawer.module.css.js';
6
6
 
7
- function DesktopNavigationDrawer({ groupId, instanceId, onClosed, submenu, }) {
8
- return (jsx(Drawer, { allowCloseOnBackgroundClick: true, showBackgroundOverlay: true, className: styles['desktop-navigation-drawer'], "data-test-selector": "desktopNavigationDrawer", groupId: groupId, instanceId: instanceId, onClosed: onClosed, position: "top", showUnder: "header", children: submenu && (jsx(PanelNavigation, { "data-test-selector": "desktopNavigationPanel", header: submenu.url
9
- ? { href: submenu.url, title: submenu.title }
10
- : submenu.title, id: `panels-${submenu.key}`, links: submenu.links }, submenu.key)) }));
7
+ function DesktopNavigationDrawer({ groupId, instanceId, linkGroup, onClosed, }) {
8
+ return (jsx(Drawer, { allowCloseOnBackgroundClick: true, showBackgroundOverlay: true, className: styles['desktop-navigation-drawer'], "data-test-selector": "desktopNavigationDrawer", groupId: groupId, instanceId: instanceId, onClosed: onClosed, position: "top", showUnder: "header", children: linkGroup && (jsx(PanelNavigation, { "data-test-selector": "desktopNavigationPanel", header: linkGroup, id: `panels-${linkGroup.key}`, linkItems: linkGroup.items }, linkGroup.key)) }));
11
9
  }
12
10
 
13
11
  export { DesktopNavigationDrawer };
@@ -1,7 +1,7 @@
1
- import { NavigationLink } from '../../shared/api/bff/model/bff.model';
1
+ import { NavigationLinkItem } from '../../shared/api/bff/model/bff.model';
2
2
  export interface MobileNavigationDrawerProps {
3
3
  groupId?: string;
4
4
  instanceId?: string;
5
- links: NavigationLink[];
5
+ linkItems: NavigationLinkItem[] | undefined;
6
6
  }
7
- export declare function MobileNavigationDrawer({ groupId, instanceId, links, }: MobileNavigationDrawerProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function MobileNavigationDrawer({ groupId, instanceId, linkItems, }: MobileNavigationDrawerProps): import("react/jsx-runtime").JSX.Element;
@@ -4,8 +4,8 @@ import { Drawer } from '../../drawer/drawer.js';
4
4
  import { MobileNavigation } from '../../navigation/mobile-navigation/mobile-navigation.js';
5
5
  import styles from './mobile-navigation-drawer.module.css.js';
6
6
 
7
- function MobileNavigationDrawer({ groupId, instanceId, links, }) {
8
- return (jsx(Drawer, { allowCloseOnBackgroundClick: true, showBackgroundOverlay: true, className: styles['mobile-navigation-drawer'], "data-test-selector": "mobileNavigationDrawer", groupId: groupId, instanceId: instanceId, position: "left", showUnder: "announcement", children: ({ close, isOpen }) => (jsx(MobileNavigation, { id: "mobile-navigation", isActive: isOpen, links: links, onActiveChange: close })) }));
7
+ function MobileNavigationDrawer({ groupId, instanceId, linkItems, }) {
8
+ return (jsx(Drawer, { allowCloseOnBackgroundClick: true, showBackgroundOverlay: true, className: styles['mobile-navigation-drawer'], "data-test-selector": "mobileNavigationDrawer", groupId: groupId, instanceId: instanceId, position: "left", showUnder: "announcement", children: ({ close, isOpen }) => (jsx(MobileNavigation, { id: "mobile-navigation", isActive: isOpen, linkItems: linkItems, onActiveChange: close })) }));
9
9
  }
10
10
 
11
11
  export { MobileNavigationDrawer };
@@ -1,7 +1,7 @@
1
- import { NavigationLink } from '../shared/api/bff/model/bff.model';
1
+ import { NavigationSection } from '../shared/api/bff/model/bff.model';
2
2
  export interface HeaderProps {
3
3
  className?: string;
4
- links: NavigationLink[];
4
+ headerNavigationSection?: NavigationSection;
5
5
  sticky?: boolean;
6
6
  }
7
- export declare function Header({ className, links, sticky }: HeaderProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function Header({ className, headerNavigationSection, sticky, }: HeaderProps): import("react/jsx-runtime").JSX.Element;
@@ -2,11 +2,11 @@
2
2
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
3
  import { useState, useEffect } from 'react';
4
4
  import clsx from 'clsx';
5
- import { config } from '../config.js';
6
5
  import { useDrawer } from '../drawer/use-drawer.js';
7
6
  import { useGlobalSearchDisclosure } from '../global-search/global-search-provider/use-search-disclosure.js';
8
7
  import { useFormattedMessage } from '../intl/use-formatted-message.js';
9
8
  import { PATHS } from '../pages/paths.js';
9
+ import { isNavigationLink, isNavigationSection, isNavigationLinkItem } from '../shared/api/bff/model/bff.model.js';
10
10
  import { useIsBreakpoint } from '../shared/hooks/use-is-breakpoint.js';
11
11
  import { useResizeObserver } from '../shared/hooks/use-resize-observer.js';
12
12
  import { useOnNavigate } from '../shared/routing/use-on-navigate.js';
@@ -23,8 +23,8 @@ import { NavigationLinkList } from './link-list/navigation-link-list.js';
23
23
  import { SonicLogo } from './sonic-logo/sonic-logo.js';
24
24
  import styles from './header.module.css.js';
25
25
 
26
- function Header({ className, links, sticky }) {
27
- const [activeSubmenu, setActiveSubmenu] = useState();
26
+ function Header({ className, headerNavigationSection, sticky, }) {
27
+ const [activeLinkGroup, setActiveLinkGroup] = useState();
28
28
  const t = useFormattedMessage();
29
29
  const isXl = useIsBreakpoint('xl');
30
30
  const { ref: headerRef } = useResizeObserver((_, size) => {
@@ -50,11 +50,11 @@ function Header({ className, links, sticky }) {
50
50
  const toggleMobileNavigation = (isActive) => {
51
51
  setMobileNavigationOpen(isActive);
52
52
  };
53
- const toggleActiveSubmenu = (link) => {
54
- if (link === activeSubmenu)
53
+ const toggleActiveSubmenu = (linkGroup) => {
54
+ if (linkGroup === activeLinkGroup)
55
55
  setDesktopNavigationOpen(false);
56
56
  else
57
- setActiveSubmenu(link);
57
+ setActiveLinkGroup(linkGroup);
58
58
  };
59
59
  const hasDrawersOpen = searchOpen || mobileNavigationOpen || desktopNavigationOpen;
60
60
  useOnNavigate(() => {
@@ -63,10 +63,16 @@ function Header({ className, links, sticky }) {
63
63
  desktopNavigationDrawer.close();
64
64
  });
65
65
  useEffect(() => {
66
- if (activeSubmenu)
66
+ if (activeLinkGroup)
67
67
  setDesktopNavigationOpen(true);
68
- }, [activeSubmenu, setDesktopNavigationOpen]);
69
- return (jsxs(Fragment, { children: [jsx("header", { ref: headerRef, className: clsx(styles['header'], sticky && styles['sticky'], className), "data-test-selector": "pageHeader", children: jsx(HeaderLayout, { hamburgerButton: jsx(HamburgerButton, { "aria-controls": "mobile-navigation", "data-test-selector": "pageHeaderHamburgerButton", isActive: mobileNavigationOpen, onActiveChange: toggleMobileNavigation }), hasDrawersOpen: hasDrawersOpen, logo: jsx(SonicLogo, { "data-test-selector": "pageHeaderLogo", href: config.HOME_PAGE_URL, title: t('Home') }), mainNavigation: jsx(NavigationLinkList, { activeLink: activeSubmenu, "data-test-selector": "pageHeaderMainNavigation", links: links, onSubmenuToggle: link => toggleActiveSubmenu(link) }), navigationActions: jsxs(Fragment, { children: [jsx(ConnectedAccountButton, { "data-test-selector": "pageHeaderAccountButton", href: PATHS.ACCOUNT }), jsx(ConnectedFavoritesButton, { "data-test-selector": "pageHeaderFavoritesButton", href: PATHS.FAVORITES }), jsx(ConnectedCartButton, { "data-test-selector": "pageHeaderCartButton", href: PATHS.CART })] }), search: jsx(SearchButton, { "aria-controls": "global-search", "data-test-selector": "pageHeaderSearchButton", isActive: searchOpen, onActiveChange: toggleSearch }) }) }), jsx(SearchDrawer, { groupId: searchGroupId, instanceId: searchInstanceId }), !isXl && (jsx(MobileNavigationDrawer, { groupId: mobileNavigationDrawer.groupId, instanceId: mobileNavigationDrawer.instanceId, links: links })), isXl && (jsx(DesktopNavigationDrawer, { groupId: desktopNavigationDrawer.groupId, instanceId: desktopNavigationDrawer.instanceId, onClosed: () => setActiveSubmenu(undefined), submenu: activeSubmenu }))] }));
68
+ }, [activeLinkGroup, setDesktopNavigationOpen]);
69
+ const homeLink = headerNavigationSection?.items
70
+ .filter(isNavigationLink)
71
+ .find(item => item.key === 'home');
72
+ const mainNavigationSection = headerNavigationSection?.items
73
+ .filter(isNavigationSection)
74
+ .find(item => item.key === 'main-navigation');
75
+ return (jsxs(Fragment, { children: [jsx("header", { ref: headerRef, className: clsx(styles['header'], sticky && styles['sticky'], className), "data-test-selector": "pageHeader", children: jsx(HeaderLayout, { hamburgerButton: jsx(HamburgerButton, { "aria-controls": "mobile-navigation", "data-test-selector": "pageHeaderHamburgerButton", isActive: mobileNavigationOpen, onActiveChange: toggleMobileNavigation }), hasDrawersOpen: hasDrawersOpen, logo: jsx(SonicLogo, { "data-test-selector": "pageHeaderLogo", href: homeLink?.href || undefined, title: t('Home') }), mainNavigation: jsx(NavigationLinkList, { activeLink: activeLinkGroup, "data-test-selector": "pageHeaderMainNavigation", navigationSection: mainNavigationSection, onSubmenuToggle: toggleActiveSubmenu }), navigationActions: jsxs(Fragment, { children: [jsx(ConnectedAccountButton, { "data-test-selector": "pageHeaderAccountButton", href: PATHS.ACCOUNT }), jsx(ConnectedFavoritesButton, { "data-test-selector": "pageHeaderFavoritesButton", href: PATHS.FAVORITES }), jsx(ConnectedCartButton, { "data-test-selector": "pageHeaderCartButton", href: PATHS.CART })] }), search: jsx(SearchButton, { "aria-controls": "global-search", "data-test-selector": "pageHeaderSearchButton", isActive: searchOpen, onActiveChange: toggleSearch }) }) }), jsx(SearchDrawer, { groupId: searchGroupId, instanceId: searchInstanceId }), !isXl && (jsx(MobileNavigationDrawer, { groupId: mobileNavigationDrawer.groupId, instanceId: mobileNavigationDrawer.instanceId, linkItems: mainNavigationSection?.items.filter(isNavigationLinkItem) })), isXl && (jsx(DesktopNavigationDrawer, { groupId: desktopNavigationDrawer.groupId, instanceId: desktopNavigationDrawer.instanceId, linkGroup: activeLinkGroup, onClosed: () => setActiveLinkGroup(undefined) }))] }));
70
76
  }
71
77
 
72
78
  export { Header };
@@ -1,8 +1,8 @@
1
- import { NavigationLink } from '../../shared/api/bff/model/bff.model';
1
+ import { NavigationLinkGroup, NavigationLinkItem, NavigationSection } from '../../shared/api/bff/model/bff.model';
2
2
  export interface LinkListProps {
3
- activeLink?: NavigationLink;
3
+ activeLink?: NavigationLinkItem;
4
4
  'data-test-selector'?: string;
5
- links: NavigationLink[];
6
- onSubmenuToggle: (link: NavigationLink) => void;
5
+ navigationSection: NavigationSection | undefined;
6
+ onSubmenuToggle: (linkGroup: NavigationLinkGroup) => void;
7
7
  }
8
- export declare function NavigationLinkList({ activeLink, 'data-test-selector': dataTestSelector, links, onSubmenuToggle, }: LinkListProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function NavigationLinkList({ activeLink, 'data-test-selector': dataTestSelector, navigationSection, onSubmenuToggle, }: LinkListProps): import("react/jsx-runtime").JSX.Element;
@@ -2,14 +2,29 @@
2
2
  import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import { Link } from '../../buttons/link/link.js';
4
4
  import { GlyphsChevronsBoldDownIcon } from '../../icons/glyph/glyphs-chevrons-bold-down-icon.js';
5
+ import { logger } from '../../logging/logger.js';
6
+ import { isNavigationLinkGroup, isNavigationLink } from '../../shared/api/bff/model/bff.model.js';
5
7
  import styles from './navigation-link-list.module.css.js';
6
8
 
7
- function NavigationLinkList({ activeLink, 'data-test-selector': dataTestSelector, links, onSubmenuToggle, }) {
8
- return (jsx("ul", { className: styles['navigation-link-list'], "data-test-selector": dataTestSelector, children: links.map(link => {
9
- if (link.links && link.links.length > 0) {
10
- return (jsx("li", { className: activeLink === link ? styles['active'] : undefined, children: jsxs(Link, { "aria-controls": `panels-${link.key}`, "aria-expanded": activeLink === link, className: styles.link, "data-test-selector": `navigationLink-${link.key}`, onClick: () => onSubmenuToggle(link), target: link.openInNewTab ? '_blank' : undefined, children: [link.title, jsx(GlyphsChevronsBoldDownIcon, { className: styles.chevron, role: "presentation" })] }) }, link.key));
9
+ function NavigationLinkList({ activeLink, 'data-test-selector': dataTestSelector, navigationSection, onSubmenuToggle, }) {
10
+ const items = navigationSection?.items ?? [];
11
+ return (jsx("ul", { className: styles['navigation-link-list'], "data-test-selector": dataTestSelector, children: items.map(item => {
12
+ if (isNavigationLinkGroup(item)) {
13
+ if (item.items.length > 0) {
14
+ return (jsx("li", { className: activeLink === item ? styles['active'] : undefined, children: jsxs(Link, { "aria-controls": `panels-${item.key}`, "aria-expanded": activeLink === item, className: styles.link, "data-test-selector": `navigationLink-${item.key}`, onClick: () => onSubmenuToggle(item), target: item.openInNewTab ? '_blank' : undefined, children: [item.label, jsx(GlyphsChevronsBoldDownIcon, { className: styles.chevron, role: "presentation" })] }) }, item.key));
15
+ }
16
+ else {
17
+ logger.warn(`Link-group has no items: ${JSON.stringify(item, null, 2)}`);
18
+ return null;
19
+ }
20
+ }
21
+ else if (isNavigationLink(item)) {
22
+ return (jsx("li", { children: jsx(Link, { className: styles.link, href: item.href, children: item.label }) }, item.key));
23
+ }
24
+ else {
25
+ logger.warn(`Unsupported link type: ${JSON.stringify(item, null, 2)}`);
26
+ return null;
11
27
  }
12
- return (jsx("li", { children: jsx(Link, { className: styles.link, href: link.url, children: link.title }) }, link.key));
13
28
  }) }));
14
29
  }
15
30