# All files / src/Navigation/components ItemGroup.tsx

20.41% Statements 10/49
0% Branches 0/35
0% Functions 0/11
22.22% Lines 10/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    13011x
    11x
    11x
    11x
    11x

    11x
    11x

    11x

    11x

    11x
     import * as React from 'react'
    import Item from './Item'
    import { ChevronDownMed as ChevronDownMedIcon } from '@fluent-ui/icons'
    import Transition from '../../Transition'
    import {
      StyledItemGroupWrapper,
      StyledItemGroupTitleWrapper,
      StyledItemGroupTitleIconWrapper,
      StyledItemGroupItemWrapper
    } from './ItemGroup.styled'
    import { NavigationItemGroupProps } from '../Navigation.type'
    import { NavigationContext } from '../Navigation'
    import { useReveal } from '@fluent-ui/hooks'

    // TODO horizontal support

    const ItemGroup = React.memo(
      ({ children, title, icon, level = 1 }: NavigationItemGroupProps): React.ReactElement => {
        const { value: activeID, expanded, reveal, acrylic } = React.useContext(NavigationContext)

        const [open, setOpen] = React.useState(false)
        const handleOpen = React.useCallback((): void => {
          if (expanded === true) setOpen((v): boolean => !v)
        }, [expanded])

        // handle active item
        const childIds = React.useMemo(
          (): number[] =>
            React.Children.map(children, (child: React.ReactElement): number => child.props.id),
          [children]
        )
        const [isActiveGroup, setIsActiveGroup] = React.useState(false)
        React.useEffect((): void => {
          if (activeID) {
            const target = childIds.find((v): boolean => v === activeID)
            if (target !== undefined) {
              setIsActiveGroup(true)
            } else {
              setIsActiveGroup(false)
            }
          }
        }, [activeID, childIds])

        // If expanded is false, set open to false
        const openRecords: React.MutableRefObject<boolean> = React.useRef(false)
        React.useEffect((): void => {
          if (expanded === true) {
            openRecords.current = isActiveGroup || open
          }
        }, [open, isActiveGroup]) // eslint-disable-line
        React.useEffect((): void => {
          if (expanded === false) {
            setOpen(false)
          }
          if (expanded === true) {
            setOpen(openRecords.current)
          }
        }, [expanded, isActiveGroup])

        // handle Reveal Effects
        const [RevealWrapper] = useReveal(66)
        const hasIcon = !!icon
        const titleElement = reveal ? (
          <RevealWrapper>
            <Item>
              {hasIcon ? (
                [
                  React.cloneElement(icon as React.ReactElement, { key: 'StyledItemGroupTitleIcon' }),
                  <span key="StyledItemGroupTitle">{title}</span>
                ]
              ) : (
                <span>{title}</span>
              )}
            </Item>
          </RevealWrapper>
        ) : (
          <Item>
            {hasIcon ? (
              [
                React.cloneElement(icon as React.ReactElement, { key: 'StyledItemGroupTitleIcon' }),
                <span key="StyledItemGroupTitle">{title}</span>
              ]
            ) : (
              <span>{title}</span>
            )}
          </Item>
        )
        const childElements = reveal
          ? React.Children.map(
              children,
              (child, i): React.ReactElement => {
                if (child.type && (child as any).type.displayName === 'FNavigationItem') {
                  return <RevealWrapper key={i}>{child}</RevealWrapper>
                } else if (child.type && (child as any).type.displayName === 'FItemGroup') {
                  return React.cloneElement(child, { level: level + 1 })
                }

                return child
              }
            )
          : React.Children.map(
              children,
              (child): React.ReactElement => {
                if (child.type && (child as any).type.displayName === 'FItemGroup') {
                  return React.cloneElement(child, { level: level + 1 })
                }
                return child
              }
            )

        return (
          <StyledItemGroupWrapper>
            <StyledItemGroupTitleWrapper onClick={handleOpen} active={isActiveGroup}>
              {titleElement}
              <StyledItemGroupTitleIconWrapper open={open} expanded={expanded} acrylic={acrylic}>
                <ChevronDownMedIcon />
              </StyledItemGroupTitleIconWrapper>
            </StyledItemGroupTitleWrapper>
            <Transition visible={open} type="collapse">
              <StyledItemGroupItemWrapper level={level}>{childElements}</StyledItemGroupItemWrapper>
            </Transition>
          </StyledItemGroupWrapper>
        )
      }
    )

    ItemGroup.displayName = 'FItemGroup'

    export default ItemGroup