diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
index f1bdb3ef2dcc..5ce824302da6 100644
--- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
+++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
@@ -7960,6 +7960,31 @@ Map {
},
"render": [Function],
},
+ "unstable_IconTab" => Object {
+ "$$typeof": Symbol(react.forward_ref),
+ "propTypes": Object {
+ "children": Object {
+ "type": "node",
+ },
+ "className": Object {
+ "type": "string",
+ },
+ "defaultOpen": Object {
+ "type": "bool",
+ },
+ "enterDelayMs": Object {
+ "type": "number",
+ },
+ "label": Object {
+ "isRequired": true,
+ "type": "node",
+ },
+ "leaveDelayMs": Object {
+ "type": "number",
+ },
+ },
+ "render": [Function],
+ },
"unstable_Layer" => Object {
"propTypes": Object {
"as": Object {
@@ -8526,6 +8551,15 @@ Map {
"contained": Object {
"type": "bool",
},
+ "iconSize": Object {
+ "args": Array [
+ Array [
+ "default",
+ "lg",
+ ],
+ ],
+ "type": "oneOf",
+ },
"light": Object {
"type": "bool",
},
diff --git a/packages/react/src/__tests__/index-test.js b/packages/react/src/__tests__/index-test.js
index 5dd2afd0ca51..c09fae710b74 100644
--- a/packages/react/src/__tests__/index-test.js
+++ b/packages/react/src/__tests__/index-test.js
@@ -203,6 +203,7 @@ Array [
"unstable_HStack",
"unstable_Heading",
"unstable_IconButton",
+ "unstable_IconTab",
"unstable_Layer",
"unstable_Menu",
"unstable_MenuDivider",
diff --git a/packages/react/src/components/Tabs/index.js b/packages/react/src/components/Tabs/index.js
index b362256537d6..6c8ba98ddf3d 100644
--- a/packages/react/src/components/Tabs/index.js
+++ b/packages/react/src/components/Tabs/index.js
@@ -6,7 +6,13 @@
*/
import * as FeatureFlags from '@carbon/feature-flags';
-import { Tabs as TabsNext, TabPanel, TabPanels, TabList } from './next/Tabs';
+import {
+ Tabs as TabsNext,
+ TabPanel,
+ TabPanels,
+ TabList,
+ IconTab,
+} from './next/Tabs';
import { default as TabsClassic } from './Tabs';
import { default as TabsSkeletonClassic } from './Tabs.Skeleton';
import { default as TabsSkeletonNext } from './next/Tabs.Skeleton';
@@ -19,6 +25,6 @@ const TabsSkeleton = FeatureFlags.enabled('enable-v11-release')
? TabsSkeletonNext
: TabsSkeletonClassic;
-export { TabsSkeleton, TabPanels, TabPanel, TabList };
+export { TabsSkeleton, TabPanels, TabPanel, TabList, IconTab };
export default Tabs;
diff --git a/packages/react/src/components/Tabs/next/Tabs.js b/packages/react/src/components/Tabs/next/Tabs.js
index b7bc48d452a6..11175ce1eac6 100644
--- a/packages/react/src/components/Tabs/next/Tabs.js
+++ b/packages/react/src/components/Tabs/next/Tabs.js
@@ -8,6 +8,7 @@
import PropTypes from 'prop-types';
import React, { useState, useRef, useEffect } from 'react';
import cx from 'classnames';
+import { Tooltip } from '../../Tooltip/next';
import { keys, match, matches } from '../../../internal/keyboard';
import { usePrefix } from '../../../internal/usePrefix';
import { useId } from '../../../internal/useId';
@@ -125,6 +126,7 @@ function TabList({
light,
scrollIntoView,
contained = false,
+ iconSize,
...rest
}) {
const {
@@ -138,6 +140,8 @@ function TabList({
const className = cx(`${prefix}--tabs`, customClassName, {
[`${prefix}--tabs--contained`]: contained,
[`${prefix}--tabs--light`]: light,
+ [`${prefix}--tabs__icon--default`]: iconSize === 'default',
+ [`${prefix}--tabs__icon--lg`]: iconSize === 'lg',
});
const tabs = [];
@@ -242,6 +246,10 @@ TabList.propTypes = {
*/
contained: PropTypes.bool,
+ /**
+ * If using `IconTab`, specify the size of the icon being used.
+ */
+ iconSize: PropTypes.oneOf(['default', 'lg']),
/**
* Specify whether or not to use the light component variant
*/
@@ -343,6 +351,71 @@ Tab.propTypes = {
renderButton: PropTypes.func,
};
+const IconTab = React.forwardRef(function IconTab(
+ {
+ children,
+ className: customClassName,
+ defaultOpen = false,
+ enterDelayMs,
+ leaveDelayMs,
+ label,
+ ...rest
+ },
+ ref
+) {
+ const prefix = usePrefix();
+
+ const classNames = cx(`${prefix}--tabs__nav-item--icon`, customClassName);
+ return (
+
+
+ {children}
+
+
+ );
+});
+
+IconTab.propTypes = {
+ /**
+ * Provide an icon to be rendered inside of `IconTab` as the visual label for Tab.
+ */
+ children: PropTypes.node,
+
+ /**
+ * Specify an optional className to be added to your Tab
+ */
+ className: PropTypes.string,
+
+ /**
+ * Specify whether the tooltip for the icon should be open when it first renders
+ */
+ defaultOpen: PropTypes.bool,
+
+ /**
+ * Specify the duration in milliseconds to delay before displaying the tooltip for the icon.
+ */
+ enterDelayMs: PropTypes.number,
+
+ /**
+ * Provide the label to be rendered inside of the Tooltip. The label will use
+ * `aria-labelledby` and will fully describe the child node that is provided.
+ * This means that if you have text in the child node it will not be
+ * announced to the screen reader.
+ */
+ label: PropTypes.node.isRequired,
+
+ /**
+ * Specify the duration in milliseconds to delay before hiding the tooltip
+ */
+ leaveDelayMs: PropTypes.number,
+};
+
const TabPanel = React.forwardRef(function TabPanel(
{ children, className: customClassName, ...rest },
forwardRef
@@ -407,7 +480,7 @@ TabPanels.propTypes = {
children: PropTypes.node,
};
-export { Tabs, Tab, TabPanel, TabPanels, TabList };
+export { Tabs, Tab, IconTab, TabPanel, TabPanels, TabList };
// TO DO: implement horizontal scroll and the following props:
// leftOverflowButtonProps
diff --git a/packages/react/src/components/Tabs/next/Tabs.stories.js b/packages/react/src/components/Tabs/next/Tabs.stories.js
index a5ec931fc9e6..afbc1f9c8344 100644
--- a/packages/react/src/components/Tabs/next/Tabs.stories.js
+++ b/packages/react/src/components/Tabs/next/Tabs.stories.js
@@ -6,11 +6,18 @@
*/
import React from 'react';
-import { Tabs, TabList, Tab, TabPanels, TabPanel } from './Tabs';
+import { Tabs, TabList, Tab, TabPanels, TabPanel, IconTab } from './Tabs';
import Button from '../../Button';
import TabsSkeleton from './Tabs.Skeleton';
-import { Monster20, Corn20, Bat20 } from '@carbon/icons-react';
+import {
+ Monster20,
+ Corn20,
+ Bat20,
+ Monster16,
+ Corn16,
+ Bat16,
+} from '@carbon/icons-react';
import { unstable_FeatureFlags as FeatureFlags } from 'carbon-components-react';
@@ -55,18 +62,39 @@ export const Default = () => (
);
+export const Icon20Only = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tab Panel 1
+ Tab Panel 2
+ Tab Panel 3
+
+
+);
+
export const IconOnly = () => (
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
Tab Panel 1
diff --git a/packages/react/src/index.js b/packages/react/src/index.js
index c6e9f18638cd..8ad409cec51b 100644
--- a/packages/react/src/index.js
+++ b/packages/react/src/index.js
@@ -247,6 +247,7 @@ export {
TabPanel as unstable_TabPanel,
TabPanels as unstable_TabPanels,
TabList as unstable_TabList,
+ IconTab as unstable_IconTab,
} from './components/Tabs';
export { usePrefix as unstable_usePrefix } from './internal/usePrefix';
export {
diff --git a/packages/styles/scss/components/tabs/_tabs.scss b/packages/styles/scss/components/tabs/_tabs.scss
index 74df93d2f43f..b57a1555c3d4 100644
--- a/packages/styles/scss/components/tabs/_tabs.scss
+++ b/packages/styles/scss/components/tabs/_tabs.scss
@@ -23,12 +23,15 @@
@use '../../utilities/rotate' as *;
@use '../../utilities/box-shadow' as *;
@use '../../utilities/component-tokens' as *;
+@use '../../utilities/custom-property';
@use '../../utilities/skeleton' as *;
@use '../../utilities/visually-hidden' as *;
@use '../../utilities/button-reset';
@use '../../utilities/high-contrast-mode' as *;
@use '../../utilities/convert' as *;
+$icon-tab-size: custom-property.get-var('icon-tab-size', rem(40px));
+
/// Tabs styles
/// @access public
/// @group tabs
@@ -244,6 +247,24 @@
text-align: left;
}
+ //-----------------------------
+ // Icon Item
+ //-----------------------------
+
+ .#{$prefix}--tabs__nav-item--icon,
+ &.#{$prefix}--tabs--contained .#{$prefix}--tabs__nav-item--icon {
+ display: flex;
+ width: $icon-tab-size;
+ height: $icon-tab-size;
+ align-items: center;
+ justify-content: center;
+ padding: 0;
+ }
+
+ &.#{$prefix}--tabs__icon--lg {
+ @include custom-property.declaration('icon-tab-size', rem(48px));
+ }
+
//-----------------------------
// Item Hover
//-----------------------------