diff --git a/packages/react/src/components/Table/TableCellRenderer/TableCellRenderer.jsx b/packages/react/src/components/Table/TableCellRenderer/TableCellRenderer.jsx
index dcac20791d..f44f52a937 100644
--- a/packages/react/src/components/Table/TableCellRenderer/TableCellRenderer.jsx
+++ b/packages/react/src/components/Table/TableCellRenderer/TableCellRenderer.jsx
@@ -1,11 +1,12 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
-import { DefinitionTooltip, Tooltip } from '@carbon/react';
+import { Tooltip } from '@carbon/react';
import warning from 'warning';
import { settings } from '../../../constants/Settings';
import { WrapCellTextPropTypes } from '../../../constants/SharedPropTypes';
+import { DefinitionTooltip } from '../../Tooltip';
const { iotPrefix } = settings;
@@ -91,6 +92,8 @@ const TableCellRenderer = ({
id="table-header-tooltip"
align={tooltipDirection}
openOnHover
+ tooltipText={tooltip}
+ as="a"
>
{element}
diff --git a/packages/react/src/components/Table/TableHead/ColumnResize.jsx b/packages/react/src/components/Table/TableHead/ColumnResize.jsx
index 97a34484d2..1cb3f01b10 100644
--- a/packages/react/src/components/Table/TableHead/ColumnResize.jsx
+++ b/packages/react/src/components/Table/TableHead/ColumnResize.jsx
@@ -19,11 +19,6 @@ const propTypes = {
paddingExtra: PropTypes.number.isRequired,
preserveColumnWidths: PropTypes.bool.isRequired,
showExpanderColumn: PropTypes.bool.isRequired,
- resizeColumnText: PropTypes.string,
-};
-
-const defaultProps = {
- resizeColumnText: 'Resize column',
};
const dragHandleWidth = 4;
@@ -96,7 +91,6 @@ const ColumnResize = React.forwardRef((props, ref) => {
paddingExtra,
showExpanderColumn,
preserveColumnWidths,
- resizeColumnText,
} = props;
const [startX, setStartX] = useState(0);
const [leftPosition, setLeftPosition] = useState(0);
@@ -175,11 +169,8 @@ const ColumnResize = React.forwardRef((props, ref) => {
}));
return (
- // eslint-disable-next-line jsx-a11y/click-events-have-key-events
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
e.stopPropagation()}
onMouseDown={(e) => onMouseDown(e)}
style={{
@@ -194,6 +185,5 @@ const ColumnResize = React.forwardRef((props, ref) => {
});
ColumnResize.propTypes = propTypes;
-ColumnResize.defaultProps = defaultProps;
export default ColumnResize;
diff --git a/packages/react/src/components/Table/TableHead/_column-resize.scss b/packages/react/src/components/Table/TableHead/_column-resize.scss
index 8926a99b4b..0d28d8dff0 100644
--- a/packages/react/src/components/Table/TableHead/_column-resize.scss
+++ b/packages/react/src/components/Table/TableHead/_column-resize.scss
@@ -14,6 +14,11 @@
&:hover {
background-color: $layer-selected-inverse;
}
+ &:focus {
+ background-color: $layer-selected-inverse;
+ outline: $spacing-01 solid $focus;
+ outline-offset: -$spacing-01;
+ }
}
.#{$iot-prefix}--column-resize-handle--dragging {
diff --git a/packages/react/src/components/Table/TableHead/_table-head.scss b/packages/react/src/components/Table/TableHead/_table-head.scss
index bc8d78de12..d1f456b99e 100644
--- a/packages/react/src/components/Table/TableHead/_table-head.scss
+++ b/packages/react/src/components/Table/TableHead/_table-head.scss
@@ -56,10 +56,6 @@
}
}
- th[aria-sort] span.#{$prefix}--popover-container {
- padding-left: 1rem;
- }
-
th button.#{$prefix}--definition-term {
font-weight: 600;
}
@@ -255,16 +251,15 @@
}
.#{$prefix}--table-sort {
- padding-left: 0;
- padding-right: 0;
+ padding-inline-start: $spacing-05;
.#{$prefix}--table-header-label,
.#{$prefix}--tooltip--definition {
- padding-left: $spacing-05;
- padding-right: 0;
+ padding-inline-start: 0;
+ padding-inline-end: 0;
+ text-decoration: none;
[dir='rtl'] & {
- padding-left: unset;
padding-right: $spacing-05;
}
}
diff --git a/packages/react/src/components/Table/TableToolbar/TableToolbar.jsx b/packages/react/src/components/Table/TableToolbar/TableToolbar.jsx
index b6a5b17ffd..6b503af266 100644
--- a/packages/react/src/components/Table/TableToolbar/TableToolbar.jsx
+++ b/packages/react/src/components/Table/TableToolbar/TableToolbar.jsx
@@ -328,73 +328,6 @@ const TableToolbar = ({
className={classnames(`${iotPrefix}--table-toolbar`, className)}
aria-label={i18n.toolbarLabelAria}
>
- {hasBatchActionToolbar ? (
-
tableTranslateWithId(i18n, ...args)}
- >
- {hasVisibleBatchActions &&
- visibleBatchActions.map(({ id, labelText, disabled, ...others }) => (
- onApplyBatchAction(id)}
- tabIndex={shouldShowBatchActions ? 0 : -1}
- disabled={!shouldShowBatchActions || disabled}
- {...others}
- >
- {labelText}
-
- ))}
- {hasVisibleOverflowBatchActions ? (
- e.stopPropagation()}
- renderIcon={(props) => }
- tabIndex={shouldShowBatchActions ? 0 : -1}
- size="md"
- menuOptionsClass={`${iotPrefix}--table-overflow-batch-actions__menu`}
- withCarbonTooltip
- tooltipPosition="bottom"
- buttonLabel={i18n.batchActionsOverflowMenuText}
- >
- {visibleOverflowBatchActions.map(
- ({
- id,
- labelText,
- disabled,
- hasDivider,
- isDelete,
- renderIcon,
- iconDescription,
- }) => (
- onApplyBatchAction(id)}
- key={`table-batch-actions-overflow-menu-${id}`}
- requireTitle={!renderIcon}
- hasDivider={hasDivider}
- isDelete={isDelete}
- aria-label={labelText}
- />
- )
- )}
-
- ) : null}
-
- ) : null}
{secondaryTitle ? (
// eslint-disable-next-line jsx-a11y/label-has-associated-control, jsx-a11y/label-has-for
@@ -613,6 +546,73 @@ const TableToolbar = ({
}
)}
+ {hasBatchActionToolbar ? (
+
tableTranslateWithId(i18n, ...args)}
+ >
+ {hasVisibleBatchActions &&
+ visibleBatchActions.map(({ id, labelText, disabled, ...others }) => (
+ onApplyBatchAction(id)}
+ tabIndex={shouldShowBatchActions ? 0 : -1}
+ disabled={!shouldShowBatchActions || disabled}
+ {...others}
+ >
+ {labelText}
+
+ ))}
+ {hasVisibleOverflowBatchActions ? (
+ e.stopPropagation()}
+ renderIcon={(props) => }
+ tabIndex={shouldShowBatchActions ? 0 : -1}
+ size="md"
+ menuOptionsClass={`${iotPrefix}--table-overflow-batch-actions__menu`}
+ withCarbonTooltip
+ tooltipPosition="bottom"
+ buttonLabel={i18n.batchActionsOverflowMenuText}
+ >
+ {visibleOverflowBatchActions.map(
+ ({
+ id,
+ labelText,
+ disabled,
+ hasDivider,
+ isDelete,
+ renderIcon,
+ iconDescription,
+ }) => (
+ onApplyBatchAction(id)}
+ key={`table-batch-actions-overflow-menu-${id}`}
+ requireTitle={!renderIcon}
+ hasDivider={hasDivider}
+ isDelete={isDelete}
+ aria-label={labelText}
+ />
+ )
+ )}
+
+ ) : null}
+
+ ) : null}
);
};
diff --git a/packages/react/src/components/Table/TableToolbar/_table-toolbar.scss b/packages/react/src/components/Table/TableToolbar/_table-toolbar.scss
index ac8945f93e..e180465474 100644
--- a/packages/react/src/components/Table/TableToolbar/_table-toolbar.scss
+++ b/packages/react/src/components/Table/TableToolbar/_table-toolbar.scss
@@ -62,6 +62,10 @@ div.#{$prefix}--toolbar-action.#{$prefix}--toolbar-search-container-expandable {
will-change: transform;
}
+.#{$prefix}--batch-actions.#{$prefix}--batch-actions--active.#{$iot-prefix}--table-batch-actions {
+ z-index: 2;
+}
+
.#{$iot-prefix}--table-toolbar-content {
flex: 1;
font-size: 0.875rem;
@@ -166,15 +170,13 @@ html[dir='rtl'] {
}
}
-.#{$iot-prefix}--table-overflow-batch-actions {
+.#{$prefix}--table-toolbar
+ .#{$prefix}--tooltip-trigger__wrapper
+ .#{$iot-prefix}--table-overflow-batch-actions {
&.#{$prefix}--overflow-menu--open,
&.#{$prefix}--overflow-menu--open:hover,
- &:hover {
- // background-color: $hover-primary;//$$hover-primary;
- }
-
&:focus {
- outline: 2px solid $layer-01;
+ outline: $spacing-01 solid $layer-01;
outline-offset: -($spacing-01);
}
diff --git a/packages/react/src/components/Tooltip/DefinitionTooltip.jsx b/packages/react/src/components/Tooltip/DefinitionTooltip.jsx
new file mode 100644
index 0000000000..1d3c3a06a7
--- /dev/null
+++ b/packages/react/src/components/Tooltip/DefinitionTooltip.jsx
@@ -0,0 +1,236 @@
+/**
+ * Copied from Carbon Design System
+ * Copyright IBM Corp. 2016, 2025
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import cx from 'classnames';
+import PropTypes from 'prop-types';
+import React, { useState } from 'react';
+import { Popover, PopoverContent } from '@carbon/react';
+
+import { match, keys } from '../../internal/keyboard';
+import { settings } from '../../constants/Settings';
+
+const { prefix: carbonPrefix } = settings;
+
+const DefinitionTooltip = ({
+ align = 'bottom',
+ autoAlign,
+ className,
+ children,
+ definition,
+ defaultOpen = false,
+ id,
+ openOnHover,
+ tooltipText,
+ triggerClassName,
+ as = 'button', // NEW: Element type or custom component
+ renderTrigger, // NEW: Custom render function (alternative)
+ ...rest
+}) => {
+ const [isOpen, setOpen] = useState(defaultOpen);
+ const prefix = carbonPrefix || 'cds';
+
+ // Generate a unique ID if not provided
+ const tooltipId = id || `definition-tooltip-${Math.random().toString(36).substr(2, 9)}`;
+
+ function onKeyDown(event) {
+ if (isOpen && match(event, keys.Escape)) {
+ event.stopPropagation();
+ setOpen(false);
+ }
+ }
+
+ // Common trigger props
+ const triggerProps = {
+ className: cx(`${prefix}--definition-term`, triggerClassName),
+ 'aria-controls': tooltipId,
+ 'aria-describedby': tooltipId,
+ 'aria-expanded': isOpen,
+ tabIndex: 0,
+ onBlur: () => {
+ setOpen(false);
+ },
+ onMouseDown: (event) => {
+ // We use onMouseDown rather than onClick to make sure this triggers
+ // before onFocus.
+ if (event.button === 0) {
+ // Prevent default for anchor tags
+ if (as === 'a' || (typeof as === 'string' && as.toLowerCase() === 'a')) {
+ event.preventDefault();
+ }
+ setOpen(!isOpen);
+ }
+ },
+ onKeyDown,
+ ...rest,
+ };
+
+ // Add onClick for anchor tags
+ if (as === 'a' || (typeof as === 'string' && as.toLowerCase() === 'a')) {
+ triggerProps.onClick = (event) => {
+ event.preventDefault();
+ setOpen(!isOpen);
+ };
+ // Add href for anchor tags if not provided
+ if (!rest.href) {
+ triggerProps.href = '#';
+ }
+ }
+
+ // Add type for button
+ if (as === 'button' || (typeof as === 'string' && as.toLowerCase() === 'button')) {
+ triggerProps.type = rest.type || 'button';
+ }
+
+ // Determine the trigger element
+ let TriggerElement;
+
+ if (renderTrigger) {
+ // Option 1: Custom render function
+ TriggerElement = () => renderTrigger(triggerProps, children, isOpen, setOpen);
+ } else if (typeof as === 'string') {
+ // Option 2: HTML element string ('button', 'a', 'span', etc.)
+ TriggerElement = () => React.createElement(as, triggerProps, children);
+ } else {
+ // Option 3: Custom React component
+ TriggerElement = () => React.createElement(as, triggerProps, children);
+ }
+
+ return (
+
{
+ setOpen(false);
+ }}
+ onMouseEnter={() => {
+ if (openOnHover) {
+ setOpen(true);
+ }
+ }}
+ onFocus={() => {
+ setOpen(true);
+ }}
+ open={isOpen}
+ >
+
+
+ {tooltipText ?? definition}
+
+
+ );
+};
+
+DefinitionTooltip.propTypes = {
+ /**
+ * Specify how the trigger should align with the tooltip
+ */
+ align: PropTypes.oneOf([
+ 'top',
+ 'top-left',
+ 'top-right',
+ 'bottom',
+ 'bottom-left',
+ 'bottom-right',
+ 'left',
+ 'left-bottom',
+ 'left-top',
+ 'right',
+ 'right-bottom',
+ 'right-top',
+ 'top-start',
+ 'top-end',
+ 'bottom-start',
+ 'bottom-end',
+ 'left-end',
+ 'left-start',
+ 'right-end',
+ 'right-start',
+ ]),
+
+ /**
+ * Will auto-align the popover. This prop is currently experimental and is
+ * subject to future changes. Requires React v17+
+ */
+ autoAlign: PropTypes.bool,
+
+ /**
+ * The element type or custom component to render as the trigger.
+ * Can be 'button', 'a', 'span', or a custom React component.
+ */
+ as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
+
+ /**
+ * The `children` prop will be used as the value that is being defined
+ */
+ children: PropTypes.node.isRequired,
+
+ /**
+ * Specify an optional className to be applied to the container node
+ */
+ className: PropTypes.string,
+
+ /**
+ * Specify whether the tooltip should be open when it first renders
+ */
+ defaultOpen: PropTypes.bool,
+
+ /**
+ * The `definition` prop is used as the content inside of the tooltip that
+ * appears when a user interacts with the element rendered by the `children`
+ * prop
+ */
+ definition: PropTypes.node.isRequired,
+
+ /**
+ * Alternative to definition prop for tooltip content
+ */
+ tooltipText: PropTypes.node,
+
+ /**
+ * Provide a value that will be assigned as the id of the tooltip
+ */
+ id: PropTypes.string,
+
+ /**
+ * Specifies whether or not the `DefinitionTooltip` should open on hover or not
+ */
+ openOnHover: PropTypes.bool,
+
+ /**
+ * Custom render function for the trigger element.
+ * Receives (props, children, isOpen, setOpen) as arguments.
+ * If provided, this takes precedence over the `as` prop.
+ */
+ renderTrigger: PropTypes.func,
+
+ /**
+ * The CSS class name of the trigger element
+ */
+ triggerClassName: PropTypes.string,
+};
+
+DefinitionTooltip.defaultProps = {
+ align: 'bottom',
+ as: 'button',
+ autoAlign: false,
+ className: undefined,
+ defaultOpen: false,
+ tooltipText: '',
+ id: undefined,
+ openOnHover: false,
+ renderTrigger: undefined,
+ triggerClassName: undefined,
+};
+
+export { DefinitionTooltip };
+export default DefinitionTooltip;
+
+// Made with Bob
diff --git a/packages/react/src/components/Tooltip/_definition-tooltip.scss b/packages/react/src/components/Tooltip/_definition-tooltip.scss
new file mode 100644
index 0000000000..39e475d601
--- /dev/null
+++ b/packages/react/src/components/Tooltip/_definition-tooltip.scss
@@ -0,0 +1,22 @@
+@use '@carbon/react/scss/components/popover' as *;
+@use '@carbon/react/scss/config' as *;
+@use '@carbon/react/scss/spacing' as *;
+@use '@carbon/react/scss/type' as *;
+@use '@carbon/react/scss/colors' as *;
+@use '@carbon/react/scss/theme' as *;
+@use '../../globals/vars' as *;
+
+// Definition Tooltip styles
+.#{$prefix}--popover-container .#{$prefix}--definition-term {
+ margin-inline-start: 0;
+ text-decoration: none;
+ .#{$iot-prefix}--table__cell-text--truncate {
+ overflow: hidden;
+ }
+}
+.#{$prefix}--popover-container .#{$prefix}--definition-term > span:first-child {
+ padding-inline-start: 0;
+}
+.#{$prefix}--popover-container .#{$prefix}--definition-term > span.cds--popover-container {
+ padding: 0;
+}
diff --git a/packages/react/src/components/Tooltip/_tooltip.scss b/packages/react/src/components/Tooltip/_tooltip.scss
index de235a8eec..a79dd2f5b0 100644
--- a/packages/react/src/components/Tooltip/_tooltip.scss
+++ b/packages/react/src/components/Tooltip/_tooltip.scss
@@ -7,6 +7,9 @@
@use '@carbon/react/scss/colors' as *;
@use '../../globals/vars' as *;
+// Import DefinitionTooltip styles
+@use './definition-tooltip';
+
.#{$iot-prefix}--tooltip {
display: inline-flex;
align-items: center;
diff --git a/packages/react/src/components/Tooltip/index.jsx b/packages/react/src/components/Tooltip/index.jsx
index 5564752f76..18e36acbea 100644
--- a/packages/react/src/components/Tooltip/index.jsx
+++ b/packages/react/src/components/Tooltip/index.jsx
@@ -88,4 +88,5 @@ Tooltip.propTypes = {
showIcon: PropTypes.bool,
};
+export { DefinitionTooltip } from './DefinitionTooltip';
export default Tooltip;
diff --git a/packages/react/src/internal/keyboard.js b/packages/react/src/internal/keyboard.js
new file mode 100644
index 0000000000..9989e92f09
--- /dev/null
+++ b/packages/react/src/internal/keyboard.js
@@ -0,0 +1,42 @@
+/**
+ * Copied from Carbon Design System
+ * Copyright IBM Corp. 2016, 2025
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+export const keys = {
+ Escape: 'Escape',
+ Enter: 'Enter',
+ Space: ' ',
+ ArrowUp: 'ArrowUp',
+ ArrowDown: 'ArrowDown',
+ ArrowLeft: 'ArrowLeft',
+ ArrowRight: 'ArrowRight',
+ Tab: 'Tab',
+};
+
+/**
+ * Check if the given event matches the provided key or keys
+ * @param {KeyboardEvent} event - The keyboard event to check
+ * @param {string|string[]} keyOrKeys - A key or array of keys to match against
+ * @returns {boolean} - True if the event key matches
+ */
+export function match(event, keyOrKeys) {
+ if (Array.isArray(keyOrKeys)) {
+ return keyOrKeys.some((key) => event.key === key);
+ }
+ return event.key === keyOrKeys;
+}
+
+/**
+ * Get the character from a keyboard event
+ * @param {KeyboardEvent} event - The keyboard event
+ * @returns {string} - The character from the event
+ */
+export function getCharacterFor(event) {
+ return event.key;
+}
+
+// Made with Bob