UNPKG

@boomerang-io/carbon-addons-boomerang-react

Version:
206 lines (203 loc) 16.4 kB
import React from 'react'; import { useQuery } from 'react-query'; import { Theme, Header as Header$1, SkipToContent, HeaderName, HeaderNavigation, HeaderMenuItem, HeaderGlobalBar, HeaderPanel, HeaderMenuButton, SideNav, SideNavItems, SideNavLink } from '@carbon/react'; import { Wikis, Checkmark, Collaborate, NotificationNew, Notification, Help, UserAvatar, Close, Switcher, OpenPanelFilledRight } from '@carbon/react/icons'; import HeaderAppSwitcher from './HeaderAppSwitcher.js'; import HeaderTeamSwitcher from './HeaderTeamSwitcher.js'; import HeaderMenu from './HeaderMenu.js'; import NotificationsContainer from '../Notifications/NotificationsContainer.js'; import PlatformNotificationsContainer from '../PlatformNotifications/PlatformNotificationsContainer.js'; import UserRequests from './UserRequests.js'; import useHeaderMenu from '../../hooks/useHeaderMenu.js'; import useWindowSize from '../../hooks/useWindowSize.js'; import { resolver, serviceUrl } from '../../config/servicesConfig.js'; import { prefix } from '../../internal/settings.js'; /* IBM Confidential 694970X, 69497O0 © Copyright IBM Corp. 2022, 2025 */ const MenuListId = { Notifcations: "header-notifications-dialog", Profile: "header-profile-menu", Requests: "header-user-requests-menu", RightPanel: "header-right-panel-dialog", SideNav: "header-sidenav-menu", Support: "header-support-menu", instanceSwitcher: "header-instanceSwitcher-menu", Switcher: "header-switcher-menu", TeamSwitcher: "header-team-switcher-menu", }; const MenuButtonId = { Notifcations: "header-notifications-dialog-button", Profile: "header-profile-menu-button", Requests: "header-user-requests-menu-button", RightPanel: "header-right-panel-dialog-button", SideNav: "header-sidenav-menu-button", Support: "header-support-menu-button", InstanceSwitcher: "header-instanceSwitcher-menu-button", Switcher: "header-switcher-menu-button", TeamSwitcher: "header-team-switcher-menu-button", }; const MenuAriaLabelRecord = { Notifcations: "Notifications dialog", Profile: "Profile menu", Requests: "Requests menu", RightPanel: "RightPanel dialog", SideNav: "SideNav menu", instanceSwitcher: "Instance Switcher Menu", Support: "Support menu", Switcher: "Switcher menu", TeamSwitcher: "Team Switcher menu", }; const headerButtonClassNames = "cds--btn--icon-only cds--header__action cds--btn cds--btn--primary cds--btn--icon-only cds--btn cds--btn--primary"; const instanceCheckMarkContainerClass = "instance-checkmark-style-container"; function Header(props) { const { analyticsHelpers, productName, baseEnvUrl, baseServicesUrl, carbonTheme = "g10", className, createJoinTeamTrigger, history, isLaunchpad = false, isLoadingTeamSwitcher, isSuccessTeamSwitcher, setIsSuccessTeamSwitcher, navLinks, platform, prefixName = "", refetchUser, refetchNavigation, rightPanel, skipToContentProps, templateMeteringEvent, trackEvent, triggerEvent, user, userTeams, userTeamsAssets, } = props; const hasUserTeams = Boolean(userTeams); const userTeamsUrl = serviceUrl.getUserTeamsServices({ baseServicesUrl }); const teamsQuery = useQuery({ queryKey: userTeamsUrl, queryFn: resolver.query(userTeamsUrl, null), enabled: !hasUserTeams && Boolean(baseServicesUrl), }); return (React.createElement(React.Fragment, null, React.createElement(Theme, { theme: carbonTheme }, React.createElement(Header$1, { "aria-label": "App navigation header", className: className }, skipToContentProps ? React.createElement(SkipToContent, { ...skipToContentProps }) : null, React.createElement(SidenavMenu, { leftPanel: props.leftPanel, navLinks: props.navLinks }), React.createElement(HeaderName, { href: `${baseEnvUrl}/launchpad`, prefix: prefixName, "data-testid": "header-product" }, productName), React.createElement(HeaderNavigation, { "aria-label": "Platform navigation" }, Array.isArray(navLinks) ? navLinks.map((link) => (React.createElement(HeaderMenuItem, { "aria-label": `Link for ${link.name}`, "data-testid": "header-menu-link", href: link.url, isCurrentPage: window?.location?.href && link.url ? window.location.href.startsWith(link.url) : false, key: link.name, target: link.isExternal ? "_blank" : undefined, rel: link.isExternal ? "noopener noreferrer" : undefined }, link.name))) : null), React.createElement(HeaderGlobalBar, null, React.createElement(HeaderTeamSwitcher, { analyticsHelpers: analyticsHelpers, baseServicesUrl: baseServicesUrl, createJoinTeamTrigger: createJoinTeamTrigger, history: history, isLaunchpad: isLaunchpad, isLoadingTeamSwitcher: isLoadingTeamSwitcher, isSuccessTeamSwitcher: isSuccessTeamSwitcher, setIsSuccessTeamSwitcher: setIsSuccessTeamSwitcher, menuAriaLabelRecord: MenuAriaLabelRecord.TeamSwitcher, menuButtonId: MenuButtonId.TeamSwitcher, menuListId: MenuListId.TeamSwitcher, navigationPlatform: platform, refetchUser: refetchUser, refetchNavigation: refetchNavigation, teamsQuery: teamsQuery, trackEvent: trackEvent, user: user, userTeams: userTeams }), props?.instanceSwitcherEnabled && (React.createElement(InstanceSwitcherMenu, { enabled: Boolean(props.instanceSwitcherEnabled), menuItems: platform?.instances })), React.createElement(RequestsMenu, { baseEnvUrl: baseEnvUrl, enabled: Boolean(props.requestSummary), summary: props.requestSummary }), React.createElement(NotificationsMenu, { baseEnvUrl: baseEnvUrl, baseServicesUrl: baseServicesUrl, enabled: Boolean(props.enableNotifications), countEnabled: Boolean(props.enableNotificationsCount) }), React.createElement(SupportMenu, { enabled: Array.isArray(props.supportMenuItems) && props.supportMenuItems.length > 0, menuItems: props.supportMenuItems }), React.createElement(ProfileMenu, { enabled: Array.isArray(props.profileMenuItems) && props.profileMenuItems.length > 0, menuItems: props.profileMenuItems }), React.createElement(AppSwitcherMenu, { baseEnvUrl: baseEnvUrl, baseServicesUrl: baseServicesUrl, enabled: props.enableAppSwitcher, templateMeteringEvent: templateMeteringEvent, triggerEvent: triggerEvent, userTeams: userTeamsAssets }), React.createElement(RightPanelMenu, { enabled: Boolean(rightPanel && Object.keys(rightPanel).length), ...rightPanel })))), React.createElement(NotificationsContainer, { enableMultiContainer: true, containerId: `${prefix}--bmrg-header-notifications` }))); } function InstanceSwitcherMenu(props) { const currentURL = window?.location?.href; const { isOpen, toggleActive, ref } = useHeaderMenu(MenuButtonId.InstanceSwitcher); if (!props.enabled) { return null; } return (React.createElement("div", { className: `${prefix}--bmrg-header-instance-switcher`, style: { position: "relative" }, ref: ref }, React.createElement("button", { "aria-controls": MenuListId.instanceSwitcher, "aria-expanded": isOpen, "aria-haspopup": "menu", "aria-label": MenuAriaLabelRecord.instanceSwitcher, className: headerButtonClassNames, "data-testid": "header-instanceSwitcher-link", id: MenuButtonId.InstanceSwitcher, onClick: toggleActive }, React.createElement(Wikis, { size: 20 })), isOpen ? (React.createElement(HeaderMenu, { "aria-labelledby": MenuButtonId.InstanceSwitcher, id: MenuListId.instanceSwitcher }, Array.isArray(props.menuItems) ? props.menuItems.map((item) => (React.createElement(HeaderMenuItem, { "aria-label": `Instance Switcher for ${item.instanceName}`, "data-testid": "header-menu-instance-switcher", href: item.url, key: item.instanceName, target: "_self", rel: "noopener noreferrer" }, React.createElement("div", { className: instanceCheckMarkContainerClass }, React.createElement("span", null, item.instanceName), item.url && currentURL?.includes(item.url) ? (React.createElement("span", null, React.createElement(Checkmark, null), " ")) : (""))))) : null)) : null)); } function RequestsMenu(props) { const { isOpen, toggleActive, ref } = useHeaderMenu(MenuButtonId.Requests); if (!props.enabled) { return null; } return (React.createElement("div", { style: { position: "relative" }, ref: ref }, React.createElement("button", { "aria-controls": MenuListId.Requests, "aria-expanded": isOpen, "aria-haspopup": "menu", "aria-label": MenuAriaLabelRecord.Requests, className: headerButtonClassNames, id: MenuButtonId.Requests, "data-testid": "header-requests-link", onClick: toggleActive }, React.createElement(Collaborate, { size: 20 })), isOpen ? (React.createElement(HeaderMenu, { "aria-labelledby": MenuButtonId.Requests, id: MenuListId.Requests }, React.createElement(UserRequests, { baseEnvUrl: props.baseEnvUrl, summary: props.summary }))) : null)); } function NotificationBadge(props) { return (React.createElement("div", { className: `${prefix}--bmrg-header-notifications-badge__icon-container` }, React.createElement(Notification, { size: 20 }), props.count > 0 && (React.createElement("span", { className: `${prefix}--bmrg-header-notifications-badge__icon-badge` }, props.count)))); } function NotificationsMenu(props) { const { isOpen, toggleActive, ref } = useHeaderMenu(MenuButtonId.Notifcations); const [hasNewNotifications, setHasNewNotifications] = React.useState(false); const [notificationsCount, setNotificationsCount] = React.useState(0); let icon = null; if (!props.enabled || !props.baseEnvUrl || !props.baseServicesUrl) { return null; } icon = hasNewNotifications ? React.createElement(NotificationNew, { size: 20 }) : React.createElement(Notification, { size: 20 }); if (props.countEnabled && hasNewNotifications) { icon = React.createElement(NotificationBadge, { count: notificationsCount }); } return (React.createElement("div", { style: { position: "relative" }, ref: ref }, React.createElement("button", { "aria-controls": MenuListId.Notifcations, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": MenuAriaLabelRecord.Notifcations, className: headerButtonClassNames, "data-testid": "header-notifications-link", id: MenuButtonId.Notifcations, onClick: toggleActive }, icon), React.createElement(PlatformNotificationsContainer, { "aria-labelledby": MenuButtonId.Notifcations, baseEnvUrl: props.baseEnvUrl, baseServicesUrl: props.baseServicesUrl, id: MenuListId.Notifcations, isOpen: isOpen, setHasNewNotifications: setHasNewNotifications, setNotificationsCount: setNotificationsCount }))); } function SupportMenu(props) { const { isOpen, toggleActive, ref } = useHeaderMenu(MenuButtonId.Support); if (!props.enabled) { return null; } return (React.createElement("div", { style: { position: "relative" }, ref: ref }, React.createElement("button", { "aria-controls": MenuListId.Support, "aria-expanded": isOpen, "aria-haspopup": "menu", "aria-label": MenuAriaLabelRecord.Support, className: headerButtonClassNames, "data-testid": "header-support-link", id: MenuButtonId.Support, onClick: toggleActive }, React.createElement(Help, { size: 20 })), isOpen ? (React.createElement(HeaderMenu, { "aria-labelledby": MenuButtonId.Support, id: MenuListId.Support }, props.menuItems)) : null)); } function ProfileMenu(props) { const { isOpen, toggleActive, ref } = useHeaderMenu(MenuButtonId.Profile); if (!props.enabled) { return null; } return (React.createElement("div", { style: { position: "relative" }, ref: ref }, React.createElement("button", { "aria-controls": MenuListId.Profile, "aria-expanded": isOpen, "aria-haspopup": "menu", "aria-label": MenuAriaLabelRecord.Profile, className: headerButtonClassNames, "data-testid": "header-profile-link", id: MenuButtonId.Profile, onClick: toggleActive }, React.createElement(UserAvatar, { size: 20 })), isOpen ? (React.createElement(HeaderMenu, { "aria-labelledby": MenuButtonId.Profile, id: MenuListId.Profile }, props.menuItems)) : null)); } function AppSwitcherMenu(props) { const { isOpen, toggleActive, ref } = useHeaderMenu(MenuButtonId.Switcher); const hasUserTeamsAssets = Boolean(props.userTeams); const userTeamsAssetsUrl = serviceUrl.getUserTeamsServicesAssets({ baseServicesUrl: props.baseServicesUrl }); const queryEnabled = isOpen && props.enabled && !hasUserTeamsAssets && Boolean(props.baseServicesUrl); const teamsAssetsQuery = useQuery({ queryKey: userTeamsAssetsUrl, queryFn: resolver.query(userTeamsAssetsUrl, null), enabled: queryEnabled, }); if (!props.enabled || !props.baseServicesUrl) { return null; } return (React.createElement("div", { ref: ref }, React.createElement("button", { "aria-controls": MenuListId.Switcher, "aria-expanded": isOpen, "aria-haspopup": "menu", "aria-label": MenuAriaLabelRecord.Switcher, className: headerButtonClassNames, "data-testid": "header-appswitcher-link", id: MenuButtonId.Switcher, onClick: toggleActive }, isOpen ? React.createElement(Close, { size: 20 }) : React.createElement(Switcher, { size: 20 })), React.createElement(HeaderAppSwitcher, { baseEnvUrl: props.baseEnvUrl, baseServicesUrl: props.baseServicesUrl, id: MenuListId.Switcher, isOpen: isOpen, teamsQuery: teamsAssetsQuery, templateMeteringEvent: props.templateMeteringEvent, triggerEvent: props.triggerEvent, userTeams: props.userTeams }))); } function RightPanelMenu(props) { const { isOpen, toggleActive, ref } = useHeaderMenu(MenuButtonId.RightPanel); if (!props.enabled) { return null; } return (React.createElement("div", { ref: ref }, React.createElement("button", { "aria-controls": MenuListId.RightPanel, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": MenuAriaLabelRecord.RightPanel, className: headerButtonClassNames, "data-testid": "header-right-panel", id: MenuButtonId.RightPanel, onClick: toggleActive }, props.icon ?? React.createElement(OpenPanelFilledRight, { size: 20 })), React.createElement(HeaderPanel, { id: MenuListId.RightPanel, role: "dialog", "aria-label": MenuAriaLabelRecord.RightPanel, expanded: isOpen }, props.component))); } function SidenavMenu(props) { const { ref, isOpen, setIsOpen, toggleActive } = useHeaderMenu(MenuButtonId.SideNav); const windowSize = useWindowSize(); const isMobileSidenavActive = windowSize.width < 1056; const closeMenu = () => { setIsOpen(false); }; if (typeof props.leftPanel === "function") { return (React.createElement("div", { ref: ref, "data-testid": "header-sidenav-menu" }, React.createElement(HeaderMenuButton, { "aria-label": isOpen ? "Close Side Nav" : "Expand Side Nav", id: MenuButtonId.SideNav, isActive: isOpen, isCollapsible: true, onClick: toggleActive }), props.leftPanel({ isOpen: isOpen, close: closeMenu, navLinks: isMobileSidenavActive ? props.navLinks : undefined, }))); } return (React.createElement("div", { ref: ref, "data-testid": "header-sidenav-menu" }, isMobileSidenavActive ? (React.createElement(React.Fragment, null, React.createElement(HeaderMenuButton, { "aria-label": isOpen ? "Close Side Nav" : "Expand Side Nav", id: MenuButtonId.SideNav, isActive: isOpen, isCollapsible: false, onClick: toggleActive }), // @ts-ignore React.createElement("div", { inert: isOpen ? undefined : "true" }, React.createElement(SideNav, { isChildOfHeader: true, "aria-label": "Side navigation", expanded: isOpen, isPersistent: false, onOverlayClick: closeMenu }, React.createElement(SideNavItems, null, props.navLinks?.map((link) => (React.createElement(SideNavLink, { large: true, "aria-label": `Link for ${link.name}`, href: link.url, isActive: window?.location?.href && link.url ? window.location.href.startsWith(link.url) : false, key: link.url + link.name }, link.name)))))))) : null)); } export { Header as default };