@@ -15,7 +15,9 @@ import React, {
1515 useState ,
1616 useRef ,
1717 useEffect ,
18+ useMemo ,
1819 forwardRef ,
20+ createContext ,
1921 type ReactNode ,
2022 type MouseEvent ,
2123 type KeyboardEvent ,
@@ -43,6 +45,7 @@ import { Close } from '@carbon/icons-react';
4345import { useEvent } from '../../internal/useEvent' ;
4446import { useMatchMedia } from '../../internal/useMatchMedia' ;
4547import { Text } from '../Text' ;
48+ import BadgeIndicator from '../BadgeIndicator' ;
4649
4750const verticalTabHeight = 64 ;
4851
@@ -1211,6 +1214,7 @@ const Tab = forwardRef<HTMLElement, TabProps>(function Tab(
12111214 onTabCloseRequest,
12121215 } = React . useContext ( TabsContext ) ;
12131216 const { index, hasSecondaryLabel, contained } = React . useContext ( TabContext ) ;
1217+ const { badgeIndicator } = React . useContext ( IconTabContext ) || { } ;
12141218 const dismissIconRef = useRef < HTMLButtonElement > ( null ) ;
12151219 const tabRef = useRef < HTMLElement > ( null ) ;
12161220 const ref = useMergedRefs ( [ forwardRef , tabRef ] ) ;
@@ -1441,6 +1445,7 @@ const Tab = forwardRef<HTMLElement, TabProps>(function Tab(
14411445 { secondaryLabel }
14421446 </ Text >
14431447 ) }
1448+ { ! disabled && badgeIndicator && < BadgeIndicator /> }
14441449 </ BaseComponent >
14451450 { /* always rendering dismissIcon so we don't lose reference to it, otherwise events do not work when switching from/to dismissable state */ }
14461451 { DismissIcon }
@@ -1504,7 +1509,15 @@ Tab.propTypes = {
15041509 * IconTab
15051510 */
15061511
1512+ const IconTabContext = createContext < { badgeIndicator ?: boolean } | false > (
1513+ false
1514+ ) ;
1515+
15071516export interface IconTabProps extends DivAttributes {
1517+ /**
1518+ * **Experimental**: Display an empty dot badge on the Tab.
1519+ */
1520+ badgeIndicator ?: boolean ;
15081521 /**
15091522 * Provide an icon to be rendered inside `IconTab` as the visual label for Tab.
15101523 */
@@ -1529,7 +1542,8 @@ export interface IconTabProps extends DivAttributes {
15291542 * Provide the label to be rendered inside the Tooltip. The label will use
15301543 * `aria-labelledby` and will fully describe the child node that is provided.
15311544 * This means that if you have text in the child node it will not be
1532- * announced to the screen reader.
1545+ * announced to the screen reader. If using the badgeIndicator then provide a
1546+ * label with describing that there is a new notification.
15331547 */
15341548 label : ReactNode ;
15351549
@@ -1541,6 +1555,7 @@ export interface IconTabProps extends DivAttributes {
15411555
15421556const IconTab = React . forwardRef < HTMLDivElement , IconTabProps > ( function IconTab (
15431557 {
1558+ badgeIndicator,
15441559 children,
15451560 className : customClassName ,
15461561 defaultOpen = false ,
@@ -1552,27 +1567,38 @@ const IconTab = React.forwardRef<HTMLDivElement, IconTabProps>(function IconTab(
15521567 ref
15531568) {
15541569 const prefix = usePrefix ( ) ;
1570+ const value = useMemo ( ( ) => ( { badgeIndicator } ) , [ badgeIndicator ] ) ;
1571+
1572+ const hasSize20 =
1573+ React . isValidElement ( children ) && children . props ?. size === 20 ;
15551574
15561575 const classNames = cx (
15571576 `${ prefix } --tabs__nav-item--icon-only` ,
1558- customClassName
1577+ customClassName ,
1578+ { [ `${ prefix } --tabs__nav-item--icon-only__20` ] : hasSize20 }
15591579 ) ;
15601580 return (
1561- < Tooltip
1562- align = "bottom"
1563- defaultOpen = { defaultOpen }
1564- className = { `${ prefix } --icon-tooltip` }
1565- enterDelayMs = { enterDelayMs }
1566- label = { label }
1567- leaveDelayMs = { leaveDelayMs } >
1568- < Tab className = { classNames } ref = { ref } { ...rest } >
1569- { children }
1570- </ Tab >
1571- </ Tooltip >
1581+ < IconTabContext . Provider value = { value } >
1582+ < Tooltip
1583+ align = "bottom"
1584+ defaultOpen = { defaultOpen }
1585+ className = { `${ prefix } --icon-tooltip` }
1586+ enterDelayMs = { enterDelayMs }
1587+ label = { label }
1588+ leaveDelayMs = { leaveDelayMs } >
1589+ < Tab className = { classNames } ref = { ref } { ...rest } >
1590+ { children }
1591+ </ Tab >
1592+ </ Tooltip >
1593+ </ IconTabContext . Provider >
15721594 ) ;
15731595} ) ;
15741596
15751597IconTab . propTypes = {
1598+ /**
1599+ * **Experimental**: Display an empty dot badge on the Tab.
1600+ */
1601+ badgeIndicator : PropTypes . bool ,
15761602 /**
15771603 * Provide an icon to be rendered inside `IconTab` as the visual label for Tab.
15781604 */
@@ -1597,7 +1623,8 @@ IconTab.propTypes = {
15971623 * Provide the label to be rendered inside the Tooltip. The label will use
15981624 * `aria-labelledby` and will fully describe the child node that is provided.
15991625 * This means that if you have text in the child node it will not be
1600- * announced to the screen reader.
1626+ * announced to the screen reader. If using the badgeIndicator then provide a
1627+ * label with describing that there is a new notification.
16011628 */
16021629 label : PropTypes . node . isRequired ,
16031630
0 commit comments