@boomerang-io/carbon-addons-boomerang-react
Version:
Carbon Addons for Boomerang apps
206 lines (203 loc) • 16.4 kB
JavaScript
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 };