@xo-union/tk-component-header-nav
Version:
322 lines • 15.2 kB
JavaScript
import React from "react";
import PropTypes from "prop-types";
import noop from "lodash/noop";
import { useClasses, useClassesMapper, compose } from "@xo-union/react-css-modules";
import classesPropType from "@xo-union/classes-prop-type";
import { useMobileMedia } from "@xo-union/tk-ui-breakpoints";
import { ThemeProvider as ButtonThemeProvider } from "@xo-union/tk-component-buttons";
import { PillBadge } from "@xo-union/tk-component-badges";
import { useGlobalKeyboardEscapeEffect, useNoScrollEffect } from "@xo-union/ui-accessibility";
import cx from "classnames";
import FocusLock from "react-focus-lock";
import useNavigationMenus from "./useNavigationMenus.js";
import { Provider } from "./Context.js";
import TopLevelNav from "./TopLevelNav/index.js";
import TopLevelButton, { TopLevelLink } from "./TopLevelButton/index.js";
import TheKnotLogo from "./TheKnotLogo/index.js";
import TopLevelList from "./TopLevelList/index.js";
import TopLevelAside, { TopLevelAsideLink } from "./TopLevelAside/index.js";
import TopLevelMessageBar from "./TopLevelMessageBar/index.js";
import { IdeasAdviceMegaMenu, InvitationsMegaMenu, PlanningToolsMegaMenu, RegistryMegaMenu, AttireRingsMegaMenu, VendorsMegaMenu, WeddingWebsiteMegaMenu } from "./MegaMenus/index.js";
import AccountSettingsMenu from "./AccountSettings/index.js";
import { AccountCTAsContainer, SignUpButton, LogInButton, LogOutButton } from "./AccountCTAs/index.js";
import Avatar from "./Avatar/index.js";
import HamburgerButton from "./HamburgerButton/index.js";
import ConversationsIcon from "./ConversationsIcon/index.js";
import Overlay from "./Overlay/index.js";
import useAnimationEnabled from "./useAnimationEnabled.js";
import styles from "./styles.module.css";
import imageIds from "./constants/imageIds/index.js";
import shouldShowPrideLogo from "../../utils/shouldShowPrideLogo/index.js";
import useHeaderClickTracker from "./useHeaderClickTracker.js";
import useWeddingVisionData from "./useWeddingVisionData.js";
import useWeddingWebsiteData from "./useWeddingWebsiteData.js";
import isRequiredAndOneOfOrNull from "../../utils/isRequiredAndOneOfOrNull.js";
import ResizeObserverNav from "./ResizeObserverNav/index.js";
import { SharedMegaMenuContainer } from "./MegaMenu/index.js";
import { menuTypes } from "./types.js";
import { provideExperiments } from "./Experiments/index.js";
import { FavoritesLink } from "./FavoritesLink/index.js";
import { useDefaultProps } from "./useDefaultProps.js";
const HeaderNav = props => {
var _membership$data, _membership$data2, _membership$data2$mem, _membership$data3;
const mainMenuID = 'header-main-menu';
const mainMenuHeadingID = 'header-main-menu-heading';
const {
membership,
showRewardsBar,
loggedIn = !!((_membership$data = membership.data) !== null && _membership$data !== void 0 && _membership$data.member),
couplePhotoUrl = (_membership$data2 = membership.data) === null || _membership$data2 === void 0 ? void 0 : (_membership$data2$mem = _membership$data2.member) === null || _membership$data2$mem === void 0 ? void 0 : _membership$data2$mem.couple_photo_url,
memberIsLoading = (_membership$data3 = membership.data) === null || _membership$data3 === void 0 ? void 0 : _membership$data3.isLoading,
analyticsProps,
activeProduct,
prideLogo = shouldShowPrideLogo(),
onClickLogOut = membership.actions.logOut,
onClickLogIn = noop,
onClickSignUp = noop,
unreadMessagesCount,
classes: propsClasses,
favorites,
links,
__openOnLoad__ = null,
__weddingVisionData__,
__weddingWebsiteData__,
__renderDraftsLink__,
__planningToolsFeaturedElements__ = 3
} = useDefaultProps(props);
const handleAnalyticsClick = useHeaderClickTracker(analyticsProps);
const animationEnabled = useAnimationEnabled();
const classes = useClasses(styles, propsClasses);
const buttonClasses = useClassesMapper(compose({
btn: classes['btn-override'],
'animated-element': classes['btn-override-animated-element']
}), propsClasses);
const mobileMedia = useMobileMedia();
const [navMenuState, navMenuActions, navMenuSelectors, navMenuRefs] = useNavigationMenus({
mobileMedia,
activeProduct,
__openOnLoad__
});
useNoScrollEffect({
enabled: navMenuState.isHamburgerOpenInMobile
});
useGlobalKeyboardEscapeEffect({
enabled: navMenuState.isHamburgerOpenInMobile || navMenuState.isAnyMenuOpen,
onEscapeKey: evt => {
evt.stopImmediatePropagation();
navMenuActions.closeLast();
}
});
const [weddingVisionData, fetchWeddingVisionData] = useWeddingVisionData(__weddingVisionData__);
const [weddingWebsiteData, fetchWeddingWebsiteData] = useWeddingWebsiteData(__weddingWebsiteData__);
const closeHamburgerOnClick = handler => event => {
if (navMenuState.isHamburgerOpen) {
navMenuActions.closeHamburger();
}
handler(event);
};
const fetchAppData = function () {
let productToFetch = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : activeProduct;
if (productToFetch === 'planning-tools') {
fetchWeddingVisionData(loggedIn);
}
if (productToFetch === 'wedding-website') {
fetchWeddingWebsiteData(loggedIn);
}
};
const context = {
mainMenuID,
mainMenuHeadingID,
classes,
mobileMedia,
navMenuActions,
navMenuSelectors,
navMenuState,
navMenuRefs,
// Move to membershipData object?
loggedIn,
weddingWebsiteData,
weddingVisionData,
fetchAppData,
memberIsLoading,
menuTypes,
links,
imageIds,
prideLogo,
activeProduct,
__renderDraftsLink__,
__planningToolsFeaturedElements__
};
return /*#__PURE__*/React.createElement(Provider, {
value: context
}, /*#__PURE__*/React.createElement(TopLevelMessageBar, {
isEnabled: showRewardsBar
}), /*#__PURE__*/React.createElement(ButtonThemeProvider, {
classes: buttonClasses
}, /*#__PURE__*/React.createElement(FocusLock, {
disabled: !navMenuState.isHamburgerOpenInMobile,
autoFocus: false,
returnFocus: true
}, /*#__PURE__*/React.createElement(ResizeObserverNav, {
"aria-label": "Header",
className: cx(classes['nav-container'], !navMenuState.isHamburgerOpen && classes['hamburger-is-closed'], navMenuState.isAnyMegaMenuOpen && classes['mega-menu-is-open'], !animationEnabled && classes['no-animation'], loggedIn && classes['is-logged-in'], memberIsLoading && classes['member-is-loading']),
isHamburgerOpenInMobile: navMenuState.isHamburgerOpenInMobile,
onClick: handleAnalyticsClick
}, /*#__PURE__*/React.createElement(HamburgerButton, null), /*#__PURE__*/React.createElement(Overlay, null, /*#__PURE__*/React.createElement("div", {
className: classes['top-level-nav-container']
}, /*#__PURE__*/React.createElement(TopLevelNav, {
headingContent: "Main menu"
}, /*#__PURE__*/React.createElement(TheKnotLogo, {
className: classes['hide-in-mobile']
}), /*#__PURE__*/React.createElement(TopLevelList, null, /*#__PURE__*/React.createElement(TopLevelButton, {
menuID: menuTypes.PLANNING_TOOLS
}, "Planning Tools"), /*#__PURE__*/React.createElement(TopLevelButton, {
menuID: menuTypes.VENDORS
}, "Vendors"), /*#__PURE__*/React.createElement(TopLevelButton, {
menuID: menuTypes.WEDDING_WEBSITE
}, "Wedding Website"), /*#__PURE__*/React.createElement(TopLevelButton, {
menuID: menuTypes.INVITATIONS
}, "Invitations"), /*#__PURE__*/React.createElement(TopLevelButton, {
menuID: menuTypes.REGISTRY
}, "Registry"), /*#__PURE__*/React.createElement(TopLevelButton, {
menuID: menuTypes.ATTIRE_RINGS
}, "Attire & Rings"), /*#__PURE__*/React.createElement(TopLevelButton, {
menuID: menuTypes.IDEAS_ADVICE
}, "Ideas & Advice"), /*#__PURE__*/React.createElement(TopLevelLink, {
href: "https://weddingshop.theknot.com/?itm_source=theknot.com&itm_medium=referral&itm_campaign=TKmainnav"
}, "Gifts & Favors")), /*#__PURE__*/React.createElement(TopLevelAside, null, loggedIn ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
className: cx(classes['account-container'], classes['hide-in-mobile'])
}, favorites !== false && /*#__PURE__*/React.createElement(FavoritesLink, favorites), /*#__PURE__*/React.createElement(ConversationsIcon, {
unreadMessagesCount: unreadMessagesCount || 0
}), /*#__PURE__*/React.createElement(TopLevelButton, {
menuID: menuTypes.ACCOUNT_SETTINGS
}, /*#__PURE__*/React.createElement(PillBadge, {
className: classes['account-settings-button']
}, /*#__PURE__*/React.createElement(Avatar, {
couplePhotoUrl: couplePhotoUrl
}), "Your account"))), /*#__PURE__*/React.createElement(AccountSettingsMenu, {
menuID: menuTypes.ACCOUNT_SETTINGS,
headingContent: "Account Settings",
couplePhotoUrl: couplePhotoUrl
}, /*#__PURE__*/React.createElement(AccountSettingsMenu.SubMenuLink, {
href: links.WEDDING_SETTINGS
}, "Account settings"), /*#__PURE__*/React.createElement(AccountSettingsMenu.SubMenuLink, {
href: "https://theknot.zendesk.com"
}, "Customer service"), /*#__PURE__*/React.createElement(LogOutButton, {
onClick: onClickLogOut
}))) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
className: classes['top-level-nav-right-aside-link-container']
}, /*#__PURE__*/React.createElement(TopLevelAsideLink, {
iconName: "guests_selected",
href: links.FIND_A_COUPLES_REGISTRY,
hideLinkInDesktop: true
}, "Find a couple"), /*#__PURE__*/React.createElement(TopLevelAsideLink, {
className: classes['hide-in-desktop'],
href: "https://helpcenter.theknot.com",
hideLinkInDesktop: true
}, "Customer service")), /*#__PURE__*/React.createElement(AccountCTAsContainer, null, /*#__PURE__*/React.createElement(LogInButton, {
onClick: closeHamburgerOnClick(onClickLogIn),
size: "sm",
hideLoadingInMobile: true
}), /*#__PURE__*/React.createElement(SignUpButton, {
onClick: closeHamburgerOnClick(onClickSignUp),
size: "sm",
hideLoadingInMobile: true
}))))), /*#__PURE__*/React.createElement(SharedMegaMenuContainer, null, /*#__PURE__*/React.createElement(PlanningToolsMegaMenu, null), /*#__PURE__*/React.createElement(VendorsMegaMenu, null), /*#__PURE__*/React.createElement(WeddingWebsiteMegaMenu, null), /*#__PURE__*/React.createElement(InvitationsMegaMenu, null), /*#__PURE__*/React.createElement(RegistryMegaMenu, null), /*#__PURE__*/React.createElement(AttireRingsMegaMenu, null), /*#__PURE__*/React.createElement(IdeasAdviceMegaMenu, null)))), /*#__PURE__*/React.createElement("div", {
className: classes['mobile-app-bar-items'],
ref: navMenuRefs.mobileAppBarItemsRef
}, /*#__PURE__*/React.createElement(TheKnotLogo, null), /*#__PURE__*/React.createElement(AccountCTAsContainer, null, loggedIn ? /*#__PURE__*/React.createElement(React.Fragment, null, favorites !== false && /*#__PURE__*/React.createElement(FavoritesLink, favorites), /*#__PURE__*/React.createElement(ConversationsIcon, {
unreadMessagesCount: unreadMessagesCount || 0
}), /*#__PURE__*/React.createElement("a", {
href: links.ACCOUNT_SETTINGS,
"aria-label": "Your account",
title: "Your account"
}, /*#__PURE__*/React.createElement(Avatar, {
couplePhotoUrl: couplePhotoUrl
}))) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(LogInButton, {
onClick: closeHamburgerOnClick(onClickLogIn),
hideLoadingInMediaSmLow: true
}), /*#__PURE__*/React.createElement(SignUpButton, {
onClick: closeHamburgerOnClick(onClickSignUp)
}))))))));
};
process.env.NODE_ENV !== "production" ? HeaderNav.propTypes = {
/**
* Whether or not to render the logged in version
*/
loggedIn: PropTypes.bool,
/**
* Sets the menu that should be shown when the hamburger menu is opened and adds an active underline for the menu button on desktop
*/
activeProduct: isRequiredAndOneOfOrNull(['planning-tools', 'vendors', 'wedding-website', 'invitations', 'registry', 'attire-rings', 'ideas-advice']),
/**
* Callback for clicking log in CTA
*/
onClickLogIn: PropTypes.func,
/**
* Callback for clicking sign up CTA
*/
onClickSignUp: PropTypes.func,
/**
* Callback for clicking log out CTA
*/
onClickLogOut: PropTypes.func,
classes: classesPropType(),
/**
* Environment to be used to determine which host for The Knot to use (www vs qa-beta). Supported values are `qa` and `production`. When not supplied, URLs default to being relative to the current host.
**/
knotHostEnv: PropTypes.oneOf(['qa', 'production']),
/**
* Whether or not to display the TK logo as the pride logo (with rainbow gradient).
* Defaults to true during June unless explicitly set
*/
prideLogo: PropTypes.bool,
/**
* Props for analytics component. See [@xo-union/tk-component-analytics](/packages/@xo-union/tk-component-analytics)
*/
analyticsProps: PropTypes.shape({
product: PropTypes.string.isRequired
}).isRequired,
/**
* Media API image id of couple photo. Will be displayed in place of generic avatar.
*/
couplePhotoUrl: PropTypes.string,
/**
* A number greater than 0 will render the ConversationsActive icons state.
*/
unreadMessagesCount: PropTypes.number,
/**
* Renders the "Your Favorites Inspiration" link (with a heart icon) in the Main navigation nav
* for all pages. By default, it avoids rendering in "https://www.theknot.com/<marketplace | review-wedding-vendors | inbox>"
* but allows opting in to render by specifying `true` or an object with `isFilled` and `href` properties.
*/
favorites: PropTypes.oneOf([PropTypes.bool, PropTypes.shape({
isFilled: PropTypes.bool,
href: PropTypes.string
})]),
/**
* Extend the list of links. Can be used to override specific links
*/
links: PropTypes.objectOf(PropTypes.string),
/**
* Override the browser location object.
* Primarily used for testing purposes.
*/
location: PropTypes.shape({
pathname: PropTypes.string,
search: PropTypes.string
}),
/**
* Membership data object
*/
membershipData: PropTypes.objectOf(PropTypes.any),
/**
* Whether or not to show the rewards bar
*/
showRewardsBar: PropTypes.bool,
/**
* Open menu on load. For internal testing purposes only, do not use in production.
*/
__openOnLoad__: PropTypes.string,
/**
* The loading state for when the member is loading on page load.
*/
memberIsLoading: PropTypes.bool,
/**
* Wedding Vision Data on load. For internal testing purposes only, do not use in production.
*/
__weddingVisionData__: PropTypes.objectOf(PropTypes.any),
/**
* Wedding Vision Data on load. For internal testing purposes only, do not use in production.
*/
__weddingWebsiteData__: PropTypes.objectOf(PropTypes.any),
/**
* Renders the "Your Drafts" item on "Invitations" tab of the Main navigation nav.
*/
__renderDraftsLink__: PropTypes.bool,
/**
* Number of featured elements to display in the Planning Tools Mega Menu.
*/
__planningToolsFeaturedElements__: PropTypes.number
} : void 0;
export default provideExperiments(HeaderNav);