@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.
@@ -1611,6 +1611,166 @@ function HeaderNavigationRegion(_ref) {
1611
1611
  HeaderNavigationRegion.propTypes = {
1612
1612
  headerNavigationService: (external_prop_types_default()).object.isRequired
1613
1613
  };
1614
+ ;// ./src/utils/common.js
1615
+ /**
1616
+ * Validate and sanitize a URL string.
1617
+ * Returns '#' for invalid/empty values to provide safe fallback anchors.
1618
+ *
1619
+ * SSR-safe: no DOM/window access.
1620
+ *
1621
+ * @param {*} url - The URL value to validate
1622
+ * @returns {string} The original URL if valid, otherwise '#'
1623
+ */
1624
+ var validateUrl = function validateUrl(url) {
1625
+ if (!url || typeof url !== 'string' || url.trim() === '') {
1626
+ return '#';
1627
+ }
1628
+ return url;
1629
+ };
1630
+ ;// ./src/components/top-ribbon/types.js
1631
+
1632
+ var NavigationLinkPropTypes = external_prop_types_default().shape({
1633
+ label: (external_prop_types_default()).string.isRequired,
1634
+ url: (external_prop_types_default()).string.isRequired,
1635
+ ariaLabel: (external_prop_types_default()).string,
1636
+ dataTestId: (external_prop_types_default()).string,
1637
+ gtmEvent: (external_prop_types_default()).object
1638
+ });
1639
+ var TopRibbonPropTypes = {
1640
+ links: external_prop_types_default().arrayOf(NavigationLinkPropTypes).isRequired,
1641
+ className: (external_prop_types_default()).string,
1642
+ ariaLabel: (external_prop_types_default()).string,
1643
+ dataTestId: (external_prop_types_default()).string
1644
+ };
1645
+ ;// ./src/components/top-ribbon/TopRibbon.styled.jsx
1646
+
1647
+ 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)."; }
1648
+ // Main container for the top ribbon navigation
1649
+ var StyledTopRibbon = /*#__PURE__*/createStyled("header", true ? {
1650
+ target: "ei9etl03"
1651
+ } : 0)( true ? {
1652
+ name: "1i3elid",
1653
+ styles: "background-color:#000000;width:100%;position:relative;@media (max-width: 1023px){display:none;}&:focus-within{outline:none;}"
1654
+ } : 0);
1655
+
1656
+ // Navigation list container
1657
+ var StyledNavigationList = /*#__PURE__*/createStyled("ul", true ? {
1658
+ target: "ei9etl02"
1659
+ } : 0)( true ? {
1660
+ name: "1qblcar",
1661
+ 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"
1662
+ } : 0);
1663
+
1664
+ // Individual navigation item container
1665
+ var StyledNavigationItem = /*#__PURE__*/createStyled("li", true ? {
1666
+ target: "ei9etl01"
1667
+ } : 0)( true ? {
1668
+ name: "qy80on",
1669
+ styles: "margin:0;padding:0;flex-shrink:0;position:relative"
1670
+ } : 0);
1671
+
1672
+ // Navigation link with hover effects
1673
+ var StyledNavigationLink = /*#__PURE__*/createStyled("a", true ? {
1674
+ target: "ei9etl00"
1675
+ } : 0)( true ? {
1676
+ name: "1pvnuug",
1677
+ 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;}}"
1678
+ } : 0);
1679
+ ;// ./src/components/top-ribbon/TopRibbon.jsx
1680
+ 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); }
1681
+ 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; }
1682
+ 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; }
1683
+ 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; }
1684
+ function TopRibbon_toPropertyKey(t) { var i = TopRibbon_toPrimitive(t, "string"); return "symbol" == TopRibbon_typeof(i) ? i : i + ""; }
1685
+ 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); }
1686
+
1687
+
1688
+
1689
+
1690
+
1691
+ /**
1692
+ * TopRibbon Navigation Component
1693
+ *
1694
+ * Renders a desktop-only navigation ribbon with:
1695
+ * - Pure black background (#000000) per Figma specifications
1696
+ * - White text links (#FFFFFF) with Figma-exact hover underlines
1697
+ * - Figma-exact typography (14px/20px, Inter font)
1698
+ * - Full WCAG 2.1 AA accessibility compliance
1699
+ * - Responsive behavior (hidden below 1024px breakpoint)
1700
+ * - CSR & SSR compatible (no direct window/document access at render time)
1701
+ *
1702
+ * @param {Object} props - Component props
1703
+ * @returns {JSX.Element|null} Rendered navigation component
1704
+ */
1705
+
1706
+ var TopRibbon = function TopRibbon(_ref) {
1707
+ var links = _ref.links,
1708
+ _ref$className = _ref.className,
1709
+ className = _ref$className === void 0 ? '' : _ref$className,
1710
+ _ref$ariaLabel = _ref.ariaLabel,
1711
+ ariaLabel = _ref$ariaLabel === void 0 ? 'Top navigation' : _ref$ariaLabel,
1712
+ _ref$dataTestId = _ref.dataTestId,
1713
+ dataTestId = _ref$dataTestId === void 0 ? 'top-ribbon-nav' : _ref$dataTestId;
1714
+ // Validate required props — safe to call in SSR
1715
+ if (!links || !Array.isArray(links) || links.length === 0) {
1716
+ return null;
1717
+ }
1718
+
1719
+ /**
1720
+ * Fires a GTM dataLayer event when a link is clicked.
1721
+ * Does NOT call preventDefault — native target="_blank" behaviour is preserved.
1722
+ * SSR-safe: guarded behind typeof window check.
1723
+ *
1724
+ * @param {Object} item - The navigation item that was clicked
1725
+ */
1726
+ var handleClick = function handleClick(item) {
1727
+ if (typeof window === 'undefined' || !Array.isArray(window.dataLayer)) return;
1728
+ window.dataLayer.push(TopRibbon_objectSpread({
1729
+ event: 'top_ribbon_click',
1730
+ link_text: item.label,
1731
+ link_url: item.url
1732
+ }, item.gtmEvent));
1733
+ };
1734
+ return /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsx)(StyledTopRibbon, {
1735
+ className: className,
1736
+ "data-testid": dataTestId,
1737
+ children: /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsx)("nav", {
1738
+ "aria-label": ariaLabel,
1739
+ role: "navigation",
1740
+ "data-testid": "".concat(dataTestId, "-nav"),
1741
+ children: /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsx)(StyledNavigationList, {
1742
+ "data-testid": "".concat(dataTestId, "-list"),
1743
+ children: links.map(function (item, index) {
1744
+ if (!item || TopRibbon_typeof(item) !== 'object') return null;
1745
+ var label = item.label,
1746
+ url = item.url,
1747
+ itemAriaLabel = item.ariaLabel,
1748
+ itemTestId = item.dataTestId;
1749
+ if (!label || !url) return null;
1750
+ var linkHref = validateUrl(url);
1751
+ var linkAriaLabel = itemAriaLabel || "Navigate to ".concat(label);
1752
+ var linkTestId = itemTestId || "".concat(dataTestId, "-item-").concat(index);
1753
+ return /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsx)(StyledNavigationItem, {
1754
+ "data-testid": "".concat(linkTestId, "-container"),
1755
+ children: /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsx)(StyledNavigationLink, {
1756
+ href: linkHref,
1757
+ target: "_blank",
1758
+ rel: "noopener noreferrer",
1759
+ onClick: function onClick() {
1760
+ return handleClick(item);
1761
+ },
1762
+ "aria-label": linkAriaLabel,
1763
+ "data-testid": linkTestId,
1764
+ children: label
1765
+ })
1766
+ }, "nav-item-".concat(index, "-").concat(label));
1767
+ })
1768
+ })
1769
+ })
1770
+ });
1771
+ };
1772
+ TopRibbon.propTypes = TopRibbonPropTypes;
1773
+ /* harmony default export */ const top_ribbon_TopRibbon = (TopRibbon);
1614
1774
  ;// ./src/header/components/HeaderView.jsx
1615
1775
 
1616
1776
 
@@ -1623,6 +1783,7 @@ HeaderNavigationRegion.propTypes = {
1623
1783
 
1624
1784
 
1625
1785
 
1786
+
1626
1787
  /**
1627
1788
  * Header shell: each region calls async getters on its service to load copy before rendering.
1628
1789
  * Layout follows docs/header-domain-language.png (logo left; utility + pill search stacked right).
@@ -1638,14 +1799,48 @@ function HeaderView(_ref) {
1638
1799
  headerAccountService = _ref.headerAccountService,
1639
1800
  headerCartService = _ref.headerCartService,
1640
1801
  headerSearchService = _ref.headerSearchService,
1641
- headerNavigationService = _ref.headerNavigationService;
1802
+ headerNavigationService = _ref.headerNavigationService,
1803
+ _ref$topRibbonLinks = _ref.topRibbonLinks,
1804
+ topRibbonLinks = _ref$topRibbonLinks === void 0 ? [{
1805
+ label: 'Hair',
1806
+ url: '/hair',
1807
+ ariaLabel: 'Shop Hair'
1808
+ }, {
1809
+ label: 'Skin',
1810
+ url: '/skin',
1811
+ ariaLabel: 'Shop Skin'
1812
+ }, {
1813
+ label: 'Body',
1814
+ url: '/body',
1815
+ ariaLabel: 'Shop Body'
1816
+ }, {
1817
+ label: 'Fragrance',
1818
+ url: '/fragrance',
1819
+ ariaLabel: 'Shop Fragrance'
1820
+ }, {
1821
+ label: 'Beauty Bio',
1822
+ url: '/beauty-bio',
1823
+ ariaLabel: 'Shop Beauty Bio'
1824
+ }, {
1825
+ label: 'Accessories',
1826
+ url: '/accessories',
1827
+ ariaLabel: 'Shop Accessories'
1828
+ }, {
1829
+ label: 'Gifts',
1830
+ url: '/gifts',
1831
+ ariaLabel: 'Shop Gifts'
1832
+ }] : _ref$topRibbonLinks;
1642
1833
  return /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsxs)(HeaderRoot, {
1643
1834
  role: "banner",
1644
1835
  "data-testid": "header-view",
1645
1836
  "data-country": country,
1646
1837
  "data-language": language,
1647
1838
  "data-locale": locale,
1648
- children: [/*#__PURE__*/(0,jsx_runtime_namespaceObject.jsxs)(TopHeaderRow, {
1839
+ children: [/*#__PURE__*/(0,jsx_runtime_namespaceObject.jsx)(top_ribbon_TopRibbon, {
1840
+ links: topRibbonLinks,
1841
+ ariaLabel: "Top navigation",
1842
+ dataTestId: "header-top-ribbon"
1843
+ }), /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsxs)(TopHeaderRow, {
1649
1844
  children: [/*#__PURE__*/(0,jsx_runtime_namespaceObject.jsx)(LogoSlot, {
1650
1845
  children: /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsx)(HeaderLogoRegion, {
1651
1846
  headerLogoService: headerLogoService
@@ -1686,7 +1881,13 @@ HeaderView.propTypes = {
1686
1881
  headerAccountService: serviceProp,
1687
1882
  headerCartService: serviceProp,
1688
1883
  headerSearchService: serviceProp,
1689
- headerNavigationService: serviceProp
1884
+ headerNavigationService: serviceProp,
1885
+ topRibbonLinks: external_prop_types_default().arrayOf(external_prop_types_default().shape({
1886
+ label: (external_prop_types_default()).string.isRequired,
1887
+ url: (external_prop_types_default()).string.isRequired,
1888
+ ariaLabel: (external_prop_types_default()).string,
1889
+ dataTestId: (external_prop_types_default()).string
1890
+ }))
1690
1891
  };
1691
1892
  ;// ./src/styles/Main.styled.js
1692
1893