import React, { type ReactNode } from 'react' import { useIntl } from '@cultureamp/i18n-react-intl' import classnames from 'classnames' import { Avatar } from '~components/Avatar' import { IconButton } from '~components/ButtonV1' import { Heading } from '~components/Heading' import { Icon } from '~components/Icon' import { Select } from '~components/Select' import { Tag } from '~components/Tag' import { useMediaQueries } from '~components/utils/useMediaQueries' import { MainActions } from './subcomponents/MainActions' import { SecondaryActions } from './subcomponents/SecondaryActions' import { type NavigationTabs, type SurveyStatus, type TitleBlockAvatarProps, type TitleBlockBreadcrumbProps, type TitleBlockProps, type TitleBlockVariant, } from './types' import { isReversed } from './utils' import styles from './TitleBlock.module.scss' const renderTag = (surveyStatus: SurveyStatus): ReactNode => { let tagVariant: React.ComponentPropsWithoutRef['variant'] if (tagVariant === 'profile') { return null } if (surveyStatus.status === 'draft') { tagVariant = 'statusDraft' } /* scheduled is actually a draft survey status that has a launch job scheduled still, we want to differentiate on the UI and render a specific tag the styles must be identical to the draft style we have similar behaviour on programs index page's table */ if (surveyStatus.status === 'scheduled') { tagVariant = 'statusClosed' } if (surveyStatus.status === 'live') { tagVariant = 'statusLive' } if (surveyStatus.status === 'closed') { tagVariant = 'statusClosed' } if (surveyStatus.status === 'sentimentPositive') { tagVariant = 'sentimentPositive' } if (surveyStatus.status === 'default') { tagVariant = 'default' } return (
{surveyStatus.text}
) } const isJSXElement = ( imageElementOrAvatarProps: JSX.Element | TitleBlockAvatarProps, ): imageElementOrAvatarProps is JSX.Element => 'props' in imageElementOrAvatarProps const renderAvatar = ( imageElementOrAvatarProps: JSX.Element | TitleBlockAvatarProps, avatarAutomationId: string, ): JSX.Element => isJSXElement(imageElementOrAvatarProps) ? (
{imageElementOrAvatarProps}
) : (
) const renderSubtitle = (subtitle: React.ReactNode, subtitleAutomationId: string): JSX.Element => (
{subtitle}
) const defaultRenderSectionTitle = ( sectionTitle?: string, sectionTitleDescription?: string, variant?: TitleBlockVariant, sectionTitleAutomationId?: string, sectionTitleDescriptionAutomationId?: string, ): JSX.Element => ( <> {sectionTitle && (
{sectionTitle}
)} {sectionTitleDescription && (
{sectionTitleDescription}
)} ) const Breadcrumb = ({ breadcrumb, automationId, textAutomationId, textDirection, }: TitleBlockBreadcrumbProps): JSX.Element => { const { path, handleClick, text, render } = breadcrumb const InnerContents = (): JSX.Element => ( <>
{text} ) if (render) { const CustomRender = render return ( ) } const TagName = path ? 'a' : 'button' return ( ) } // We want to accept undefined here because the NavigationTabs container is // important for the flex-based layout (it pushes Secondary Actions over to the right) const renderNavigationTabs = ( navigationTabs: NavigationTabs | undefined, collapse: boolean, ariaLabel: string, ): JSX.Element => (
{!collapse && navigationTabs !== undefined && ( <> )}
) /** * {@link https://cultureamp.atlassian.net/wiki/spaces/DesignSystem/pages/3075605782/Title+Block Guidance} | * {@link https://cultureamp.design/?path=/docs/components-titleblock-api-specification--docs Storybook} */ export const TitleBlock = ({ title, variant, breadcrumb, avatar, subtitle, sectionTitle, sectionTitleDescription, renderSectionTitle, pageSwitcherSelect, handleHamburgerClick, primaryAction, defaultAction, secondaryActions, secondaryOverflowMenuItems, navigationTabs, collapseNavigationAreaWhenPossible = false, textDirection, surveyStatus, id, titleAutomationId = 'TitleBlock__Title', avatarAutomationId = 'TitleBlock__Avatar', subtitleAutomationId = 'TitleBlock__Subtitle', sectionTitleAutomationId = 'TitleBlock__SectionTitle', sectionTitleDescriptionAutomationId = 'TitleBlock__SectionTitleDescription', breadcrumbAutomationId = 'TitleBlock__Breadcrumb', breadcrumbTextAutomationId = 'TitleBlock__BreadcrumbText', }: TitleBlockProps): JSX.Element => { const hasNavigationTabs = navigationTabs && navigationTabs.length > 0 const collapseNavigationArea = collapseNavigationAreaWhenPossible && !hasNavigationTabs && secondaryActions === undefined const { queries: { isSmall, isMedium }, } = useMediaQueries() const isSmallOrMediumViewport = isMedium || isSmall const { formatMessage } = useIntl() return ( <>
= 30 && styles.hasLongTitle, subtitle && typeof subtitle === 'string' && subtitle.length >= 18 && styles.hasLongSubtitle, hasNavigationTabs && styles.hasNavigationTabs, )} >
{breadcrumb && ( )}
<> {handleHamburgerClick && (
} label={formatMessage({ id: 'kzTitleBlock.hamburgerMenuLabel', defaultMessage: 'Open menu', description: 'Label for the dropdown menu which displays navigation items', })} reversed={isReversed(variant)} />
)} {avatar && renderAvatar(avatar, avatarAutomationId)}
{title}
{isSmallOrMediumViewport && pageSwitcherSelect && (
)}
{(primaryAction ?? defaultAction ?? secondaryActions) && ( )}
{(sectionTitle ?? sectionTitleDescription ?? renderSectionTitle) && (
{renderSectionTitle ? renderSectionTitle({ sectionTitle, sectionTitleAutomationId, sectionTitleDescription, sectionTitleDescriptionAutomationId, }) : defaultRenderSectionTitle( sectionTitle, sectionTitleDescription, variant, sectionTitleAutomationId, sectionTitleDescriptionAutomationId, )}
)} {!sectionTitle && !sectionTitleDescription && !renderSectionTitle && renderNavigationTabs(navigationTabs, collapseNavigationArea, title)} {(secondaryActions ?? secondaryOverflowMenuItems) && ( )}
) } TitleBlock.displayName = 'TitleBlock'