@nuskin/nextgen-header 1.6.1 → 1.7.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.
@@ -1581,6 +1581,166 @@ function HeaderNavigationRegion(_ref) {
1581
1581
  HeaderNavigationRegion.propTypes = {
1582
1582
  headerNavigationService: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.object.isRequired
1583
1583
  };
1584
+ ;// ./src/utils/common.js
1585
+ /**
1586
+ * Validate and sanitize a URL string.
1587
+ * Returns '#' for invalid/empty values to provide safe fallback anchors.
1588
+ *
1589
+ * SSR-safe: no DOM/window access.
1590
+ *
1591
+ * @param {*} url - The URL value to validate
1592
+ * @returns {string} The original URL if valid, otherwise '#'
1593
+ */
1594
+ var validateUrl = function validateUrl(url) {
1595
+ if (!url || typeof url !== 'string' || url.trim() === '') {
1596
+ return '#';
1597
+ }
1598
+ return url;
1599
+ };
1600
+ ;// ./src/components/top-ribbon/types.js
1601
+
1602
+ var NavigationLinkPropTypes = __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.shape({
1603
+ label: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string.isRequired,
1604
+ url: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string.isRequired,
1605
+ ariaLabel: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string,
1606
+ dataTestId: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string,
1607
+ gtmEvent: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.object
1608
+ });
1609
+ var TopRibbonPropTypes = {
1610
+ links: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.arrayOf(NavigationLinkPropTypes).isRequired,
1611
+ className: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string,
1612
+ ariaLabel: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string,
1613
+ dataTestId: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string
1614
+ };
1615
+ ;// ./src/components/top-ribbon/TopRibbon.styled.jsx
1616
+
1617
+ function TopRibbon_styled_EMOTION_STRINGIFIED_CSS_ERROR_() { return "You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop)."; }
1618
+ // Main container for the top ribbon navigation
1619
+ var StyledTopRibbon = /*#__PURE__*/createStyled("header", true ? {
1620
+ target: "ei9etl03"
1621
+ } : 0)( true ? {
1622
+ name: "1i3elid",
1623
+ styles: "background-color:#000000;width:100%;position:relative;@media (max-width: 1023px){display:none;}&:focus-within{outline:none;}"
1624
+ } : 0);
1625
+
1626
+ // Navigation list container
1627
+ var StyledNavigationList = /*#__PURE__*/createStyled("ul", true ? {
1628
+ target: "ei9etl02"
1629
+ } : 0)( true ? {
1630
+ name: "1qblcar",
1631
+ styles: "margin:0;padding:0;list-style:none;display:flex;align-items:center;justify-content:center;gap:24px;padding:10px 24px;width:100%;box-sizing:border-box"
1632
+ } : 0);
1633
+
1634
+ // Individual navigation item container
1635
+ var StyledNavigationItem = /*#__PURE__*/createStyled("li", true ? {
1636
+ target: "ei9etl01"
1637
+ } : 0)( true ? {
1638
+ name: "qy80on",
1639
+ styles: "margin:0;padding:0;flex-shrink:0;position:relative"
1640
+ } : 0);
1641
+
1642
+ // Navigation link with hover effects
1643
+ var StyledNavigationLink = /*#__PURE__*/createStyled("a", true ? {
1644
+ target: "ei9etl00"
1645
+ } : 0)( true ? {
1646
+ name: "1pvnuug",
1647
+ styles: "display:inline-block;padding:0;cursor:pointer;color:#ffffff;font-family:\"Inter\",-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;font-size:14px;font-weight:400;line-height:20px;text-align:center;white-space:nowrap;text-decoration:underline;text-decoration-style:solid;text-decoration-color:transparent;transition:text-decoration-color 0.2s ease,background-color 0.2s ease;position:relative;&:hover{color:#ffffff;text-decoration-color:#ffffff;}&:focus-visible{outline:2px solid #ffffff;outline-offset:2px;background-color:rgba(255, 255, 255, 0.1);text-decoration-color:#ffffff;}&:focus:not(:focus-visible){outline:none;background-color:transparent;}&:active{background-color:rgba(255, 255, 255, 0.05);}@media (prefers-contrast: high){&:focus-visible{outline-color:currentColor;}}"
1648
+ } : 0);
1649
+ ;// ./src/components/top-ribbon/TopRibbon.jsx
1650
+ function TopRibbon_typeof(o) { "@babel/helpers - typeof"; return TopRibbon_typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, TopRibbon_typeof(o); }
1651
+ function TopRibbon_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
1652
+ function TopRibbon_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? TopRibbon_ownKeys(Object(t), !0).forEach(function (r) { TopRibbon_defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : TopRibbon_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
1653
+ function TopRibbon_defineProperty(e, r, t) { return (r = TopRibbon_toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
1654
+ function TopRibbon_toPropertyKey(t) { var i = TopRibbon_toPrimitive(t, "string"); return "symbol" == TopRibbon_typeof(i) ? i : i + ""; }
1655
+ function TopRibbon_toPrimitive(t, r) { if ("object" != TopRibbon_typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != TopRibbon_typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
1656
+
1657
+
1658
+
1659
+
1660
+
1661
+ /**
1662
+ * TopRibbon Navigation Component
1663
+ *
1664
+ * Renders a desktop-only navigation ribbon with:
1665
+ * - Pure black background (#000000) per Figma specifications
1666
+ * - White text links (#FFFFFF) with Figma-exact hover underlines
1667
+ * - Figma-exact typography (14px/20px, Inter font)
1668
+ * - Full WCAG 2.1 AA accessibility compliance
1669
+ * - Responsive behavior (hidden below 1024px breakpoint)
1670
+ * - CSR & SSR compatible (no direct window/document access at render time)
1671
+ *
1672
+ * @param {Object} props - Component props
1673
+ * @returns {JSX.Element|null} Rendered navigation component
1674
+ */
1675
+
1676
+ var TopRibbon = function TopRibbon(_ref) {
1677
+ var links = _ref.links,
1678
+ _ref$className = _ref.className,
1679
+ className = _ref$className === void 0 ? '' : _ref$className,
1680
+ _ref$ariaLabel = _ref.ariaLabel,
1681
+ ariaLabel = _ref$ariaLabel === void 0 ? 'Top navigation' : _ref$ariaLabel,
1682
+ _ref$dataTestId = _ref.dataTestId,
1683
+ dataTestId = _ref$dataTestId === void 0 ? 'top-ribbon-nav' : _ref$dataTestId;
1684
+ // Validate required props — safe to call in SSR
1685
+ if (!links || !Array.isArray(links) || links.length === 0) {
1686
+ return null;
1687
+ }
1688
+
1689
+ /**
1690
+ * Fires a GTM dataLayer event when a link is clicked.
1691
+ * Does NOT call preventDefault — native target="_blank" behaviour is preserved.
1692
+ * SSR-safe: guarded behind typeof window check.
1693
+ *
1694
+ * @param {Object} item - The navigation item that was clicked
1695
+ */
1696
+ var handleClick = function handleClick(item) {
1697
+ if (typeof window === 'undefined' || !Array.isArray(window.dataLayer)) return;
1698
+ window.dataLayer.push(TopRibbon_objectSpread({
1699
+ event: 'top_ribbon_click',
1700
+ link_text: item.label,
1701
+ link_url: item.url
1702
+ }, item.gtmEvent));
1703
+ };
1704
+ return /*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsx__(StyledTopRibbon, {
1705
+ className: className,
1706
+ "data-testid": dataTestId,
1707
+ children: /*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsx__("nav", {
1708
+ "aria-label": ariaLabel,
1709
+ role: "navigation",
1710
+ "data-testid": "".concat(dataTestId, "-nav"),
1711
+ children: /*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsx__(StyledNavigationList, {
1712
+ "data-testid": "".concat(dataTestId, "-list"),
1713
+ children: links.map(function (item, index) {
1714
+ if (!item || TopRibbon_typeof(item) !== 'object') return null;
1715
+ var label = item.label,
1716
+ url = item.url,
1717
+ itemAriaLabel = item.ariaLabel,
1718
+ itemTestId = item.dataTestId;
1719
+ if (!label || !url) return null;
1720
+ var linkHref = validateUrl(url);
1721
+ var linkAriaLabel = itemAriaLabel || "Navigate to ".concat(label);
1722
+ var linkTestId = itemTestId || "".concat(dataTestId, "-item-").concat(index);
1723
+ return /*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsx__(StyledNavigationItem, {
1724
+ "data-testid": "".concat(linkTestId, "-container"),
1725
+ children: /*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsx__(StyledNavigationLink, {
1726
+ href: linkHref,
1727
+ target: "_blank",
1728
+ rel: "noopener noreferrer",
1729
+ onClick: function onClick() {
1730
+ return handleClick(item);
1731
+ },
1732
+ "aria-label": linkAriaLabel,
1733
+ "data-testid": linkTestId,
1734
+ children: label
1735
+ })
1736
+ }, "nav-item-".concat(index, "-").concat(label));
1737
+ })
1738
+ })
1739
+ })
1740
+ });
1741
+ };
1742
+ TopRibbon.propTypes = TopRibbonPropTypes;
1743
+ /* harmony default export */ const top_ribbon_TopRibbon = (TopRibbon);
1584
1744
  ;// ./src/header/components/HeaderView.jsx
1585
1745
 
1586
1746
 
@@ -1593,6 +1753,7 @@ HeaderNavigationRegion.propTypes = {
1593
1753
 
1594
1754
 
1595
1755
 
1756
+
1596
1757
  /**
1597
1758
  * Header shell: each region calls async getters on its service to load copy before rendering.
1598
1759
  * Layout follows docs/header-domain-language.png (logo left; utility + pill search stacked right).
@@ -1608,14 +1769,48 @@ function HeaderView(_ref) {
1608
1769
  headerAccountService = _ref.headerAccountService,
1609
1770
  headerCartService = _ref.headerCartService,
1610
1771
  headerSearchService = _ref.headerSearchService,
1611
- headerNavigationService = _ref.headerNavigationService;
1772
+ headerNavigationService = _ref.headerNavigationService,
1773
+ _ref$topRibbonLinks = _ref.topRibbonLinks,
1774
+ topRibbonLinks = _ref$topRibbonLinks === void 0 ? [{
1775
+ label: 'Hair',
1776
+ url: '/hair',
1777
+ ariaLabel: 'Shop Hair'
1778
+ }, {
1779
+ label: 'Skin',
1780
+ url: '/skin',
1781
+ ariaLabel: 'Shop Skin'
1782
+ }, {
1783
+ label: 'Body',
1784
+ url: '/body',
1785
+ ariaLabel: 'Shop Body'
1786
+ }, {
1787
+ label: 'Fragrance',
1788
+ url: '/fragrance',
1789
+ ariaLabel: 'Shop Fragrance'
1790
+ }, {
1791
+ label: 'Beauty Bio',
1792
+ url: '/beauty-bio',
1793
+ ariaLabel: 'Shop Beauty Bio'
1794
+ }, {
1795
+ label: 'Accessories',
1796
+ url: '/accessories',
1797
+ ariaLabel: 'Shop Accessories'
1798
+ }, {
1799
+ label: 'Gifts',
1800
+ url: '/gifts',
1801
+ ariaLabel: 'Shop Gifts'
1802
+ }] : _ref$topRibbonLinks;
1612
1803
  return /*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsxs__(HeaderRoot, {
1613
1804
  role: "banner",
1614
1805
  "data-testid": "header-view",
1615
1806
  "data-country": country,
1616
1807
  "data-language": language,
1617
1808
  "data-locale": locale,
1618
- children: [/*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsxs__(TopHeaderRow, {
1809
+ children: [/*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsx__(top_ribbon_TopRibbon, {
1810
+ links: topRibbonLinks,
1811
+ ariaLabel: "Top navigation",
1812
+ dataTestId: "header-top-ribbon"
1813
+ }), /*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsxs__(TopHeaderRow, {
1619
1814
  children: [/*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsx__(LogoSlot, {
1620
1815
  children: /*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_179142b8_jsx__(HeaderLogoRegion, {
1621
1816
  headerLogoService: headerLogoService
@@ -1656,7 +1851,13 @@ HeaderView.propTypes = {
1656
1851
  headerAccountService: serviceProp,
1657
1852
  headerCartService: serviceProp,
1658
1853
  headerSearchService: serviceProp,
1659
- headerNavigationService: serviceProp
1854
+ headerNavigationService: serviceProp,
1855
+ topRibbonLinks: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.arrayOf(__WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.shape({
1856
+ label: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string.isRequired,
1857
+ url: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string.isRequired,
1858
+ ariaLabel: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string,
1859
+ dataTestId: __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31_default__.string
1860
+ }))
1660
1861
  };
1661
1862
  ;// ./src/styles/Main.styled.js
1662
1863