- This is some tooltip text. This box shows the maximum amount of text
- that should appear inside. If more room is needed please use a modal
- instead.
-
- This is some tooltip text. This box shows the maximum amount of text
- that should appear inside. If more room is needed please use a modal
- instead.
-
- This is some tooltip text. This box shows the maximum amount of text
- that should appear inside. If more room is needed please use a modal
- instead.
-
- This is some tooltip text. This box shows the maximum amount of text
- that should appear inside. If more room is needed please use a modal
- instead.
-
- This is some tooltip text. This box shows the maximum amount of text
- that should appear inside. If more room is needed please use a modal
- instead.
-
- This is some tooltip text. This box shows the maximum amount of text
- that should appear inside. If more room is needed please use a modal
- instead.
-
- This is some tooltip text. This box shows the maximum amount of text
- that should appear inside. If more room is needed please use a modal
- instead.
-
- This is some tooltip text. This box shows the maximum amount of text
- that should appear inside. If more room is needed please use a modal
- instead.
-
- This is some tooltip text. This box shows the maximum amount of text
- that should appear inside. If more room is needed please use a modal
- instead.
-
-
- );
- const label = wrapper.find(`.${prefix}--tooltip__label`);
- const floatingMenu = wrapper.find(FloatingMenu);
-
- describe('tooltip container', () => {
- it("sets the tooltip's position", () => {
- expect(floatingMenu.prop('menuDirection')).toEqual('bottom');
- });
- it("sets the tooltip's offset", () => {
- expect(floatingMenu.prop('menuOffset')).toEqual({ left: 10, top: 15 });
- });
- it('does not render info icon', () => {
- const icon = label.find(Information);
- expect(icon.exists()).toBe(false);
- });
- it('sets the tooltip class', () => {
- expect(
- floatingMenu
- .find('[data-floating-menu-direction]')
- .first()
- .prop('className')
- ).toBe(
- `${prefix}--tooltip ${prefix}--tooltip--shown ${prefix}--tooltip--bottom ${prefix}--tooltip--align-center tooltip--class`
- );
- });
- it('sets the trigger class', () => {
- expect(label.prop('className')).toBe(
- `${prefix}--tooltip__label tooltip--trigger-class`
- );
- });
- });
- });
-
- describe('Renders as expected when an Icon component wrapped with forwardRef is provided', () => {
- const wrapper = mount();
-
- it('does render Icon', () => {
- const icon = wrapper.find(Add);
- expect(icon.exists()).toBe(true);
- });
- });
-
- describe('Renders as expected when custom icon component with forwardRef is provided', () => {
- const wrapper = mount(
- (
-
- ))}
- />
- );
-
- it('does render provided custom icon component instance', () => {
- const icon = wrapper.find(CustomIcon);
- expect(icon.exists()).toBe(true);
- });
- });
-
- describe('Renders as expected when custom icon component with inner forwardRef is provided', () => {
- const wrapper = mount();
-
- it('does render provided custom icon component instance', () => {
- const icon = wrapper.find(OverflowMenuVertical);
- expect(icon.exists()).toBe(true);
- });
- });
-
- describe('events', () => {
- it('A different key press does not change state', () => {
- const wrapper = mount();
- const icon = wrapper.find(Information);
- icon.simulate('keyDown', { which: 'x' });
- // Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
- expect(wrapper.find('Tooltip').instance().state.open).toBeFalsy();
- });
-
- it('A different key press does not change state when custom icon is set', () => {
- const wrapper = mount(
- (
-
- ))}
- triggerText="Tooltip"
- />
- );
- const icon = wrapper.find('.custom-icon');
- icon.simulate('keyDown', { which: 'x' });
- // Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
- expect(wrapper.find('Tooltip').instance().state.open).toBeFalsy();
- });
-
- it('should be in a closed state after handleOutsideClick() is invoked', () => {
- const rootWrapper = mount();
- // Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
- expect(rootWrapper.find('Tooltip').instance().state.open).toBeFalsy();
- // Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
- rootWrapper.find('Tooltip').instance().setState({ open: true });
- rootWrapper.update();
- rootWrapper.find('Tooltip').instance().handleClickOutside();
- // Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
- expect(rootWrapper.find('Tooltip').instance().state.open).toEqual(false);
- });
-
- it('prop.open change should update open state', () => {
- const rootWrapper = mount();
- // Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
- expect(rootWrapper.find('Tooltip').instance().state.open).toEqual(false);
- rootWrapper.setProps({
- open: true,
- triggerText: 'Tooltip',
- });
- // Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
- expect(rootWrapper.find('Tooltip').instance().state.open).toEqual(true);
- });
-
- it('should avoid change the open state upon setting props, unless there the value actually changes', () => {
- const rootWrapper = mount();
- rootWrapper.setProps({ open: true });
- // Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
- rootWrapper.find('Tooltip').instance().setState({ open: false });
- rootWrapper.update();
- rootWrapper.setProps({ open: true });
- // Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
- expect(rootWrapper.find('Tooltip').instance().state.open).toEqual(false);
- });
-
- it('escape key keyDown should not bubble outside the tooltip', () => {
- const onKeyDown = jest.fn();
- render(
- <>
- {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
-
-
-
- >
- );
-
- userEvent.click(screen.getAllByRole('button')[0]);
- userEvent.keyboard('{esc}');
-
- expect(onKeyDown).not.toHaveBeenCalled();
- });
-
- it('should close the tooltip when escape key is pressed', () => {
- render(
-
-
tooltip body
-
- );
-
- expect(screen.queryByText('trigger text')).toBeInTheDocument();
- expect(screen.queryByText('tooltip body')).not.toBeInTheDocument();
-
- const triggerButton = screen.getByRole('button');
- userEvent.click(triggerButton);
- // I am unsure why, but the trigger must be clicked a second time for the tooltip body to appear
- userEvent.click(triggerButton);
-
- expect(screen.queryByText('tooltip body')).toBeInTheDocument();
-
- userEvent.keyboard('{esc}');
-
- expect(screen.queryByText('tooltip body')).not.toBeInTheDocument();
- });
-
- it('should not call onChange on focus of an interactive element in body when controlled', () => {
- const onChange = jest.fn();
- function ControlledWithStateOnChange() {
- const [tipOpen, setTipOpen] = useState(false);
- const handleChange = (ev, { open }) => {
- onChange(ev, { open });
- setTipOpen(open);
- };
-
- return (
-
-
- This is some tooltip text. This box shows the maximum amount of
- text that should be displayed inside. If more room is needed, use
- a modal instead.
-
-
- Learn more
-
-
-
- );
- }
-
- render();
-
- expect(
- screen.queryByText('ControlledWithStateOnChange label')
- ).toBeInTheDocument();
- expect(
- screen.queryByRole('button', { name: 'Create' })
- ).not.toBeInTheDocument();
-
- // The trigger to open the tooltip
- userEvent.click(screen.getByRole('button'));
-
- expect(onChange).toHaveBeenCalledTimes(2);
- expect(onChange).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({
- type: 'focus',
- }),
- expect.objectContaining({
- open: false,
- })
- );
- expect(onChange).toHaveBeenNthCalledWith(
- 2,
- expect.objectContaining({
- type: 'click',
- }),
- expect.objectContaining({
- open: true,
- })
- );
-
- expect(
- screen.queryByRole('button', { name: 'Create' })
- ).toBeInTheDocument();
- userEvent.click(screen.getByRole('button', { name: 'Create' }));
- expect(
- screen.queryByRole('button', { name: 'Create' })
- ).not.toBeInTheDocument();
- expect(onChange).toHaveBeenCalledTimes(2);
- expect(onChange).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({
- type: 'focus',
- target: expect.objectContaining({
- className: `${prefix}--tooltip__trigger`,
- }),
- }),
- expect.objectContaining({
- open: false,
- })
- );
- expect(onChange).toHaveBeenNthCalledWith(
- 2,
- expect.objectContaining({
- type: 'click',
- target: expect.objectContaining({
- className: `${prefix}--tooltip__trigger`,
- }),
- }),
- expect.objectContaining({
- open: true,
- })
- );
- });
- });
-});
diff --git a/packages/react/src/components/Tooltip/Tooltip.js b/packages/react/src/components/Tooltip/Tooltip.js
index 15a3bd17ab22..304920b9392e 100644
--- a/packages/react/src/components/Tooltip/Tooltip.js
+++ b/packages/react/src/components/Tooltip/Tooltip.js
@@ -5,744 +5,171 @@
* LICENSE file in the root directory of this source tree.
*/
-import React, { Component } from 'react';
+import cx from 'classnames';
import PropTypes from 'prop-types';
-import { isForwardRef } from 'react-is';
-import debounce from 'lodash.debounce';
-import classNames from 'classnames';
-import { Information } from '@carbon/icons-react';
-import FloatingMenu, {
- DIRECTION_LEFT,
- DIRECTION_TOP,
- DIRECTION_RIGHT,
- DIRECTION_BOTTOM,
-} from '../../internal/FloatingMenu';
-import ClickListener from '../../internal/ClickListener';
-import mergeRefs from '../../tools/mergeRefs';
-import { keys, matches as keyDownMatch } from '../../internal/keyboard';
-import isRequiredOneOf from '../../prop-types/isRequiredOneOf';
-import requiredIfValueExists from '../../prop-types/requiredIfValueExists';
-import { useControlledStateWithValue } from '../../internal/FeatureFlags';
-import { PrefixContext } from '../../internal/usePrefix';
+import React, { useRef, useEffect } from 'react';
+import { Popover, PopoverContent } from '../Popover';
+import { keys, match } from '../../internal/keyboard';
+import { useDelayedState } from '../../internal/useDelayedState';
+import { useId } from '../../internal/useId';
+import {
+ useNoInteractiveChildren,
+ getInteractiveContent,
+} from '../../internal/useNoInteractiveChildren';
+import { usePrefix } from '../../internal/usePrefix';
+
+function Tooltip({
+ align = 'top',
+ className: customClassName,
+ children,
+ label,
+ description,
+ enterDelayMs = 100,
+ leaveDelayMs = 300,
+ defaultOpen = false,
+ ...rest
+}) {
+ const containerRef = useRef(null);
+ const tooltipRef = useRef(null);
+ const [open, setOpen] = useDelayedState(defaultOpen);
+ const id = useId('tooltip');
+ const prefix = usePrefix();
+ const child = React.Children.only(children);
+
+ const triggerProps = {
+ onFocus: () => setOpen(true),
+ onBlur: () => setOpen(false),
+ // This should be placed on the trigger in case the element is disabled
+ onMouseEnter,
+ };
-/**
- * @param {Element} menuBody The menu body with the menu arrow.
- * @param {string} menuDirection Where the floating menu menu should be placed relative to the trigger button.
- * @returns {FloatingMenu~offset} The adjustment of the floating menu position, upon the position of the menu arrow.
- * @private
- */
-const getMenuOffset = (menuBody, menuDirection) => {
- const arrowStyle = menuBody.ownerDocument.defaultView.getComputedStyle(
- menuBody,
- ':before'
- );
- const arrowPositionProp = {
- [DIRECTION_LEFT]: 'right',
- [DIRECTION_TOP]: 'bottom',
- [DIRECTION_RIGHT]: 'left',
- [DIRECTION_BOTTOM]: 'top',
- }[menuDirection];
- const menuPositionAdjustmentProp = {
- [DIRECTION_LEFT]: 'left',
- [DIRECTION_TOP]: 'top',
- [DIRECTION_RIGHT]: 'left',
- [DIRECTION_BOTTOM]: 'top',
- }[menuDirection];
- const values = [arrowPositionProp, 'border-bottom-width'].reduce(
- (o, name) => ({
- ...o,
- [name]: Number(
- (/^([\d-]+)px$/.exec(arrowStyle.getPropertyValue(name)) || [])[1]
- ),
- }),
- {}
- );
- values[arrowPositionProp] = values[arrowPositionProp] || -6; // IE, etc.
- if (Object.keys(values).every((name) => !isNaN(values[name]))) {
- const {
- [arrowPositionProp]: arrowPosition,
- 'border-bottom-width': borderBottomWidth,
- } = values;
- return {
- left: 0,
- top: 0,
- [menuPositionAdjustmentProp]:
- Math.sqrt(Math.pow(borderBottomWidth, 2) * 2) - arrowPosition,
- };
+ if (label) {
+ triggerProps['aria-labelledby'] = id;
+ } else {
+ triggerProps['aria-describedby'] = id;
}
-};
-class Tooltip extends Component {
- constructor(props) {
- super(props);
- this.isControlled = props.open !== undefined;
- if (useControlledStateWithValue && this.isControlled) {
- // Skips the logic of setting initial state if this component is controlled
- return;
+ function onKeyDown(event) {
+ if (open && match(event, keys.Escape)) {
+ event.stopPropagation();
+ setOpen(false);
}
- const open = useControlledStateWithValue ? props.defaultOpen : props.open;
- this.state = {
- open,
- storedDirection: props.direction,
- storedAlign: props.align,
- };
}
- static propTypes = {
- /**
- * Specify the alignment (to the trigger button) of the tooltip.
- * Can be one of: start, center, or end.
- */
- align: PropTypes.oneOf(['start', 'center', 'end']),
-
- /**
- * Whether or not to re-orientate the tooltip if it goes outside,
- * of the bounds of the parent.
- */
- autoOrientation: PropTypes.bool,
- /**
- * Contents to put into the tooltip.
- */
- children: PropTypes.node,
-
- /**
- * The CSS class names of the tooltip.
- */
- className: PropTypes.string,
-
- /**
- * Optional starting value for uncontrolled state
- */
- defaultOpen: PropTypes.bool,
-
- /**
- * Where to put the tooltip, relative to the trigger UI.
- */
- direction: PropTypes.oneOf(['bottom', 'top', 'left', 'right']),
-
- /**
- * Enable or disable focus trap behavior
- */
- focusTrap: PropTypes.bool,
-
- /**
- * The name of the default tooltip icon.
- */
- iconName: PropTypes.string,
-
- /**
- * The adjustment of the tooltip position.
- */
- menuOffset: PropTypes.oneOfType([
- PropTypes.shape({
- top: PropTypes.number,
- left: PropTypes.number,
- }),
- PropTypes.func,
- ]),
-
- /**
- * * the signature of the event handler will be:
- * * `onChange(event, { open })` where:
- * * `event` is the (React) raw event
- * * `open` is the new value
- */
- onChange: !useControlledStateWithValue
- ? PropTypes.func
- : requiredIfValueExists(PropTypes.func),
-
- /**
- * Open/closed state.
- */
- open: PropTypes.bool,
-
- /**
- * The callback function to optionally render the icon element.
- * It should be a component with React.forwardRef().
- */
- renderIcon: function (props, propName, componentName) {
- if (props[propName] == undefined) {
- return;
- }
- const RefForwardingComponent = props[propName];
- if (!isForwardRef()) {
- return new Error(`Invalid value of prop '${propName}' supplied to '${componentName}',
- it should be created/wrapped with React.forwardRef() to have a ref and access the proper
- DOM node of the element to calculate its position in the viewport.`);
- }
- },
-
- /**
- * Specify a CSS selector that matches the DOM element that should
- * be focused when the Tooltip opens
- */
- selectorPrimaryFocus: PropTypes.string,
-
- /**
- * `true` to show the default tooltip icon.
- */
- showIcon: PropTypes.bool,
+ function onMouseEnter() {
+ setOpen(true, enterDelayMs);
+ }
- /**
- * Optional prop to specify the tabIndex of the Tooltip
- */
- tabIndex: PropTypes.number,
+ function onMouseLeave() {
+ setOpen(false, leaveDelayMs);
+ }
- /**
- * The ID of the tooltip body content.
- */
- tooltipBodyId: PropTypes.string,
+ useNoInteractiveChildren(
+ tooltipRef,
+ 'The Tooltip component must have no interactive content rendered by the' +
+ '`label` or `description` prop'
+ );
- /**
- * The ID of the tooltip content.
- */
- tooltipId: PropTypes.string,
+ useEffect(() => {
+ const interactiveContent = getInteractiveContent(containerRef.current);
+ if (!interactiveContent) {
+ setOpen(false);
+ }
+ });
+
+ return (
+
+ {React.cloneElement(child, triggerProps)}
+
+ {label || description}
+
+
+ );
+}
- /**
- * The CSS class names of the trigger UI.
- */
- triggerClassName: PropTypes.string,
+Tooltip.propTypes = {
+ /**
+ * Specify how the trigger should align with the tooltip
+ */
+ align: PropTypes.oneOf([
+ 'top',
+ 'top-left',
+ 'top-right',
- /**
- * The ID of the trigger button.
- */
- triggerId: PropTypes.string,
+ 'bottom',
+ 'bottom-left',
+ 'bottom-right',
- ...isRequiredOneOf({
- /**
- * The content to put into the trigger UI, except the (default) tooltip icon.
- */
- triggerText: PropTypes.node,
- /**
- * The description of the default tooltip icon, to be put in its SVG 'aria-label' and 'alt' .
- */
- iconDescription: PropTypes.string,
- }),
- };
+ 'left',
+ 'left-bottom',
+ 'left-top',
- static defaultProps = {
- align: 'center',
- direction: DIRECTION_BOTTOM,
- focusTrap: true,
- renderIcon: Information,
- showIcon: true,
- triggerText: null,
- menuOffset: getMenuOffset,
- selectorPrimaryFocus: '[data-tooltip-primary-focus]',
- };
+ 'right',
+ 'right-bottom',
+ 'right-top',
+ ]),
/**
- * The element of the tooltip body.
- * @type {Element}
- * @private
+ * Pass in the child to which the tooltip will be applied
*/
- _tooltipEl = null;
+ children: PropTypes.node,
/**
- * The element ref of the tooltip's trigger button.
- * @type {React.RefObject}
- * @private
+ * Specify an optional className to be applied to the container node
*/
- _triggerRef = React.createRef();
+ className: PropTypes.string,
/**
- * Unique tooltip ID that is user-provided or auto-generated
- * Referenced in aria-labelledby attribute
- * @type {string}
- * @private
+ * Specify whether the tooltip should be open when it first renders
*/
- _tooltipId =
- this.props.id ||
- this.props.tooltipId ||
- `__carbon-tooltip_${Math.random().toString(36).substr(2)}`;
+ defaultOpen: PropTypes.bool,
/**
- * Internal flag for tracking whether or not focusing on the tooltip trigger
- * should automatically display the tooltip body
+ * Provide the description to be rendered inside of the Tooltip. The
+ * description will use `aria-describedby` and will describe the child node
+ * in addition to the text rendered inside of the child. This means that if you
+ * have text in the child node, that it will be announced alongside the
+ * description to the screen reader.
+ *
+ * Note: if label and description are both provided, label will be used and
+ * description will not be used
*/
- _tooltipDismissed = false;
-
- componentDidMount() {
- if (!this._debouncedHandleFocus) {
- this._debouncedHandleFocus = debounce(this._handleFocus, 200);
- }
- }
-
- componentDidUpdate(prevProps, prevState) {
- if (prevProps.direction != this.props.direction) {
- this.setState({ storedDirection: this.props.direction });
- }
- if (prevProps.align != this.props.align) {
- this.setState({ storedAlign: this.props.align });
- }
- if (prevState.open && !this.state.open) {
- // Reset orientation when closing
- this.setState({
- storedDirection: this.props.direction,
- storedAlign: this.props.align,
- });
- }
- }
-
- updateOrientation = (params) => {
- if (this.props.autoOrientation) {
- const newOrientation = this.getBestDirection(params);
- const { direction, align } = newOrientation;
-
- if (direction !== this.state.storedDirection) {
- this.setState({ open: false }, () => {
- this.setState({ open: true, storedDirection: direction });
- });
- }
-
- if (align === 'original') {
- this.setState({ storedAlign: this.props.align });
- } else {
- this.setState({ storedAlign: align });
- }
- }
- };
-
- getBestDirection = ({
- menuSize,
- refPosition = {},
- offset = {},
- direction = DIRECTION_BOTTOM,
- scrollX: pageXOffset = 0,
- scrollY: pageYOffset = 0,
- container,
- }) => {
- const {
- left: refLeft = 0,
- top: refTop = 0,
- right: refRight = 0,
- bottom: refBottom = 0,
- } = refPosition;
- const scrollX = container.position !== 'static' ? 0 : pageXOffset;
- const scrollY = container.position !== 'static' ? 0 : pageYOffset;
- const relativeDiff = {
- top: container.position !== 'static' ? container.rect.top : 0,
- left: container.position !== 'static' ? container.rect.left : 0,
- };
- const { width, height } = menuSize;
- const { top = 0, left = 0 } = offset;
- const refCenterHorizontal = (refLeft + refRight) / 2;
- const refCenterVertical = (refTop + refBottom) / 2;
-
- // Calculate whether a new direction is needed to stay in parent.
- // It will switch the current direction to the opposite i.e.
- // If the direction="top" and the top boundary is overflowed
- // then it switches the direction to "bottom".
- const newDirection = () => {
- switch (direction) {
- case DIRECTION_LEFT:
- return refLeft - width + scrollX - left - relativeDiff.left < 0
- ? DIRECTION_RIGHT
- : direction;
- case DIRECTION_TOP:
- return refTop - height + scrollY - top - relativeDiff.top < 0
- ? DIRECTION_BOTTOM
- : direction;
- case DIRECTION_RIGHT:
- return refRight + scrollX + left - relativeDiff.left + width >
- container.rect.width
- ? DIRECTION_LEFT
- : direction;
- case DIRECTION_BOTTOM:
- return refBottom + scrollY + top - relativeDiff.top + height >
- container.rect.height
- ? DIRECTION_TOP
- : direction;
- default:
- // If there is a new direction then ignore the logic above
- return direction;
- }
- };
-
- // Calculate whether a new alignment is needed to stay in parent
- // If the direction is left or right this involves checking the
- // overflow in the vertical direction. If the direction is top or
- // bottom, this involves checking overflow in the horizontal direction.
- // "original" is used to signify no change.
- const newAlignment = () => {
- switch (direction) {
- case DIRECTION_LEFT:
- case DIRECTION_RIGHT:
- if (
- refCenterVertical -
- height / 2 +
- scrollY +
- top -
- 9 -
- relativeDiff.top <
- 0
- ) {
- // If goes above the top boundary
- return 'start';
- } else if (
- refCenterVertical -
- height / 2 +
- scrollY +
- top -
- 9 -
- relativeDiff.top +
- height >
- container.rect.height
- ) {
- // If goes below the bottom boundary
- return 'end';
- } else {
- // No need to change alignment
- return 'original';
- }
- case DIRECTION_TOP:
- case DIRECTION_BOTTOM:
- if (
- refCenterHorizontal -
- width / 2 +
- scrollX +
- left -
- relativeDiff.left <
- 0
- ) {
- // If goes below the left boundary
- return 'start';
- } else if (
- refCenterHorizontal -
- width / 2 +
- scrollX +
- left -
- relativeDiff.left +
- width >
- container.rect.width
- ) {
- // If it goes over the right boundary
- return 'end';
- } else {
- // No need to change alignment
- return 'original';
- }
- default:
- // No need to change alignment
- return 'original';
- }
- };
-
- return {
- direction: newDirection(),
- align: newAlignment(),
- };
- };
-
- componentWillUnmount() {
- if (this._debouncedHandleFocus) {
- this._debouncedHandleFocus.cancel();
- this._debouncedHandleFocus = null;
- }
- }
-
- static getDerivedStateFromProps({ open }, state) {
- /**
- * so that tooltip can be controlled programmatically through this `open` prop
- */
- const { prevOpen } = state;
- return prevOpen === open
- ? null
- : {
- open,
- prevOpen: open,
- };
- }
-
- _handleUserInputOpenClose = (event, { open }) => {
- if (this.isControlled && this.props.onChange) {
- // Callback to the parent to let them decide what to do
- this.props.onChange(event, { open });
- return;
- }
- // capture tooltip body element before it is removed from the DOM
- const tooltipBody = this._tooltipEl;
- this.setState({ open }, () => {
- if (this.props.onChange) {
- this.props.onChange(event, { open });
- }
- if (!open && tooltipBody && tooltipBody.id === this._tooltipId) {
- this._tooltipDismissed = true;
- const currentActiveNode = event?.relatedTarget;
- if (
- !currentActiveNode &&
- document.activeElement === document.body &&
- event?.type !== 'click'
- ) {
- this._triggerRef?.current.focus();
- }
- }
- });
- };
+ description: PropTypes.node,
/**
- * Handles `focus`/`blur` event.
- * @param {string} state `over` to show the tooltip, `out` to hide the tooltip.
- * @param {Element} [evt] For handing `mouseout` event, indicates where the mouse pointer is gone.
+ * Specify the duration in milliseconds to delay before displaying the tooltip
*/
- _handleFocus = (state, evt) => {
- const { currentTarget, relatedTarget } = evt;
- if (currentTarget !== relatedTarget) {
- this._tooltipDismissed = false;
- }
- if (state === 'over' && !this.isControlled) {
- if (!this._tooltipDismissed) {
- this._handleUserInputOpenClose(evt, { open: true });
- }
- this._tooltipDismissed = false;
- } else if (state !== 'out') {
- // Note: SVGElement in IE11 does not have `.contains()`
- const { current: triggerEl } = this._triggerRef;
- const shouldPreventClose =
- relatedTarget &&
- ((triggerEl && triggerEl?.contains(relatedTarget)) ||
- (this._tooltipEl && this._tooltipEl.contains(relatedTarget)));
- if (!shouldPreventClose) {
- this._handleUserInputOpenClose(evt, { open: false });
- }
- }
- };
+ enterDelayMs: PropTypes.number,
/**
- * The debounced version of the `focus`/`blur` event handler.
- * @type {Function}
- * @private
+ * 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, that it will not be
+ * announced to the screen reader.
+ *
+ * Note: if label and description are both provided, description will not be
+ * used
*/
- _debouncedHandleFocus = null;
+ label: PropTypes.node,
/**
- * @returns {Element} The DOM element where the floating menu is placed in.
+ * Specify the duration in milliseconds to delay before hiding the tooltip
*/
- _getTarget = () => {
- const { current: triggerEl } = this._triggerRef;
- return (
- (triggerEl && triggerEl.closest('[data-floating-menu-container]')) ||
- document.body
- );
- };
-
- handleMouse = (evt) => {
- evt.persist();
- const state = {
- focus: 'over',
- blur: 'out',
- click: 'click',
- }[evt.type];
- const hadContextMenu = this._hasContextMenu;
- if (evt.type === 'click' || evt.type === 'contextmenu') {
- this._hasContextMenu = evt.type === 'contextmenu';
- }
-
- if (this._hasContextMenu) {
- this._handleUserInputOpenClose(evt, { open: false });
- return;
- }
-
- if (state === 'click') {
- evt.preventDefault();
- const shouldOpen = this.isControlled
- ? !this.props.open
- : !this.state.open;
- this._handleUserInputOpenClose(evt, { open: shouldOpen });
- } else if (state && (state !== 'out' || !hadContextMenu)) {
- this?._debouncedHandleFocus(state, evt);
- }
- };
-
- handleClickOutside = (evt) => {
- const shouldPreventClose =
- evt &&
- evt.target &&
- this._tooltipEl &&
- this._tooltipEl.contains(evt.target);
- if (!shouldPreventClose && this.state.open) {
- this._handleUserInputOpenClose(evt, { open: false });
- }
- };
-
- handleKeyPress = (event) => {
- if (keyDownMatch(event, [keys.Escape, keys.Tab])) {
- event.stopPropagation();
- this._handleUserInputOpenClose(event, { open: false });
- }
-
- if (keyDownMatch(event, [keys.Enter, keys.Space])) {
- event.stopPropagation();
- event.preventDefault();
- const shouldOpen = this.isControlled
- ? !this.props.open
- : !this.state.open;
- this._handleUserInputOpenClose(event, { open: shouldOpen });
- }
- };
-
- handleEscKeyPress = (event) => {
- const { open } = this.isControlled ? this.props : this.state;
- if (open && keyDownMatch(event, [keys.Escape])) {
- event.stopPropagation();
- return this._handleUserInputOpenClose(event, { open: false });
- }
- };
-
- render() {
- const {
- triggerId = (this.triggerId =
- this.triggerId ||
- `__carbon-tooltip-trigger_${Math.random().toString(36).substr(2)}`),
- tooltipBodyId,
- children,
- className,
- triggerClassName,
- focusTrap,
- triggerText,
- showIcon,
- iconName,
- iconDescription,
- renderIcon: IconCustomElement,
- menuOffset,
- tabIndex = 0,
- innerRef: ref,
- selectorPrimaryFocus, // eslint-disable-line
- tooltipId, //eslint-disable-line
- autoOrientation, //eslint-disable-line
- ...other
- } = this.props;
-
- const { open } = this.isControlled ? this.props : this.state;
- const { storedDirection, storedAlign } = this.state;
-
- return (
-
- {(prefix) => {
- const tooltipClasses = classNames(
- `${prefix}--tooltip`,
- {
- [`${prefix}--tooltip--shown`]: open,
- [`${prefix}--tooltip--${storedDirection}`]: storedDirection,
- [`${prefix}--tooltip--align-${storedAlign}`]: storedAlign,
- },
- className
- );
-
- const triggerClasses = classNames(
- `${prefix}--tooltip__label`,
- triggerClassName
- );
-
- const refProp = mergeRefs(this._triggerRef, ref);
- const iconProperties = {
- name: iconName,
- role: null,
- description: null,
- };
-
- const properties = {
- role: 'button',
- tabIndex: tabIndex,
- onClick: this.handleMouse,
- onContextMenu: this.handleMouse,
- onKeyDown: this.handleKeyPress,
- onMouseOver: this.handleMouse,
- onMouseOut: this.handleMouse,
- onFocus: this.handleMouse,
- onBlur: this.handleMouse,
- 'aria-controls': !open ? undefined : this._tooltipId,
- 'aria-expanded': open,
- 'aria-describedby': open ? this._tooltipId : null,
- // if the user provides property `triggerText`,
- // then the button should use aria-labelledby to point to its id,
- // if the user doesn't provide property `triggerText`,
- // then an aria-label will be provided via the `iconDescription` property.
- ...(triggerText
- ? {
- 'aria-labelledby': triggerId,
- }
- : {
- 'aria-label': iconDescription,
- }),
- };
-
- return (
- <>
-
- {showIcon ? (
-
- {triggerText}
-
-
-
-
- ) : (
-
- {triggerText}
-
- )}
-
- {open && (
- {
- this._tooltipEl = node;
- }}
- updateOrientation={this.updateOrientation}>
- {/* This rule is disabled because the onKeyDown event handler is only
- * being used to capture and prevent the event from bubbling: */}
- {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
-
-
-
- {children}
-
-
-
- )}
- >
- );
- }}
-
- );
- }
-}
+ leaveDelayMs: PropTypes.number,
+};
export { Tooltip };
-export default (() => {
- const forwardRef = (props, ref) => ;
- forwardRef.displayName = 'Tooltip';
- return React.forwardRef(forwardRef);
-})();
diff --git a/packages/react/src/components/Tooltip/Tooltip.mdx b/packages/react/src/components/Tooltip/Tooltip.mdx
index 0749eae36ce4..6626ac612b82 100644
--- a/packages/react/src/components/Tooltip/Tooltip.mdx
+++ b/packages/react/src/components/Tooltip/Tooltip.mdx
@@ -1,4 +1,4 @@
-import { Props } from '@storybook/addon-docs';
+import { Story, ArgsTable, Canvas } from '@storybook/addon-docs';
# Tooltip
@@ -8,25 +8,113 @@ import { Props } from '@storybook/addon-docs';
|
[Accessibility](https://www.carbondesignsystem.com/components/tooltip/accessibility)
+
+
+
## Table of Contents
+- [Overview](#overview)
+ - [Toggletips vs Tooltips](#toggletips-vs-tooltips)
+ - [Customizing the content of a tooltip](#customizing-the-content-of-a-tooltip)
+ - [Tooltip alignment](#tooltip-alignment)
+ - [Customizing the duration of a tooltip](#customizing-the-duration-of-a-tooltip)
+- [Component API](#component-api)
+- [Feedback](#feedback)
+
+
+
## Overview
-### `data-floating-menu-container`
+You can use the `Tooltip` component as a wrapper to display a popup for an
+interactive element that you provide. If you are trying to place interactive
+content inside of the `Tooltip` itself, consider using `Toggletip` instead. You
+can provide the interactive element as a child to `Tooltip`, for example:
-`Tooltip` uses React Portals to render the tooltip into the DOM. To determine
-where in the DOM the menu will be placed, it looks for a parent element with the
-`data-floating-menu-container` attribute. If no parent with this attribute is
-found, the menu will be placed on `document.body`.
+```jsx
+
+
+
+```
-## Component API
+`Tooltip` accepts a single child for its `children` prop. It is important that
+the component you provide renders an interactive element. In addition, you can
+specify the contents of the popup through the `label` or `description` prop.
+
+
+
+### Toggletips vs Tooltips
+
+Toggletips and tooltips are similar visually and both contain a popover and
+interactive trigger element. The two components differ in the way they are
+invoked and dismissed and if the user must interact with the contents. A tooltip
+is exposed on hover or focus when you need to expose brief, supplemental
+information that is not interactive. A toggletip is used on click or enter when
+you need to expose interactive elements, such as button, that a user needs to
+interact with.
+
+### Customizing the content of a tooltip
+
+The `Tooltip` component supports tooltip customization through the `label` and
+`description` props. Under the hood, `label` will map to `aria-labelledby` and
+`description` will map to `aria-describedby`.
+
+It's important to consider the type of content that you provide through the
+tooltip in order to know whether to use `label` or `description`. If the
+information shown is the only content that describes your component, you should
+use a `label`. If the information is secondary or adds additional information,
+`description` would be a good pick.
+
+While the `label` and `description` props accept any arbitrary React element,
+it's important that the value you pass to either prop has no interactive
+content. This will cause a violation in the component as the semantics of this
+content will not be accessible to users of screen reader software.
+
+### Tooltip alignment
-Please note that in addition to the props below, `Tooltip` also has two
-additional props: `triggerText` and `iconDescription`. If the `triggerText` prop
-is _not_ provided, the `iconDescription` prop is required to populate the
-`aria-label` property for a11y reasons.
+The `align` prop allows you to specify where your content should be placed
+relative to the tooltip. For example, if you provide `align="top"` to the
+`Tooltip` component then the tooltip will render above your component.
+Similarly, if you provide `align="bottom"` then the tooltip will render below
+your component.
+
+
+
+You can also configure the placement of the caret, if it is enabled, by choosing
+between `left` and `right` or `bottom` and `top`, depending on where your
+tooltip is being rendered.
+
+If you are using `align="top"`, then you can choose between `align="top-left"`
+and `align="top-right"`. These options will place the caret closer to the left
+or right edges of the tooltip.
+
+If you are using `align="left"` or `align="right"`, you can use `top` or
+`bottom` to control this alignment.
+
+### Customizing the duration of a tooltip
+
+You can customize the delay between when a tooltip is invoked and its contents
+are shown. You can also customize the delay between when a tooltip is dismissed
+and its contents are hidden.
+
+
+
+The `enterDelayMs` prop allows you to provide a time, in milliseconds, that the
+component will wait before showing the tooltip. The `exitDelayMs` prop allows
+you to provide a time, in milliseconds, that the component will wait before
+hiding the tooltip.
+
+By default, these values are set to 100ms for `enterDelayms` and 300ms for
+`exitDelayMs`.
+
+## Component API
-
+
## Feedback
diff --git a/packages/react/src/components/Tooltip/next/Tooltip.stories.js b/packages/react/src/components/Tooltip/Tooltip.stories.js
similarity index 100%
rename from packages/react/src/components/Tooltip/next/Tooltip.stories.js
rename to packages/react/src/components/Tooltip/Tooltip.stories.js
diff --git a/packages/react/src/components/Tooltip/next/__tests__/DefinitionTooltip-test.js b/packages/react/src/components/Tooltip/__tests__/DefinitionTooltip-test.js
similarity index 97%
rename from packages/react/src/components/Tooltip/next/__tests__/DefinitionTooltip-test.js
rename to packages/react/src/components/Tooltip/__tests__/DefinitionTooltip-test.js
index 474ca48e2876..b93766f75e29 100644
--- a/packages/react/src/components/Tooltip/next/__tests__/DefinitionTooltip-test.js
+++ b/packages/react/src/components/Tooltip/__tests__/DefinitionTooltip-test.js
@@ -8,7 +8,7 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
-import { DefinitionTooltip } from '../../next/DefinitionTooltip';
+import { DefinitionTooltip } from '../DefinitionTooltip';
describe('DefintiionTooltip', () => {
it('should display onClick a defintion provided via prop', () => {
diff --git a/packages/react/src/components/Tooltip/next/__tests__/Tooltip-test.js b/packages/react/src/components/Tooltip/__tests__/Tooltip-test.js
similarity index 97%
rename from packages/react/src/components/Tooltip/next/__tests__/Tooltip-test.js
rename to packages/react/src/components/Tooltip/__tests__/Tooltip-test.js
index b0930979448b..cb7963038258 100644
--- a/packages/react/src/components/Tooltip/next/__tests__/Tooltip-test.js
+++ b/packages/react/src/components/Tooltip/__tests__/Tooltip-test.js
@@ -7,7 +7,7 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
-import { Tooltip } from '../../next';
+import { Tooltip } from '../';
describe('Tooltip', () => {
it('should support a custom className with the `className` prop', () => {
diff --git a/packages/react/src/components/Tooltip/index.js b/packages/react/src/components/Tooltip/index.js
index 1738e93f2eaf..e4fd5f092da9 100644
--- a/packages/react/src/components/Tooltip/index.js
+++ b/packages/react/src/components/Tooltip/index.js
@@ -5,4 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
-export default from './Tooltip';
+import { DefinitionTooltip } from './DefinitionTooltip';
+import { Tooltip } from './Tooltip';
+
+export { DefinitionTooltip, Tooltip };
diff --git a/packages/react/src/components/Tooltip/next/Tooltip.js b/packages/react/src/components/Tooltip/next/Tooltip.js
deleted file mode 100644
index 6d5956a142ff..000000000000
--- a/packages/react/src/components/Tooltip/next/Tooltip.js
+++ /dev/null
@@ -1,175 +0,0 @@
-/**
- * Copyright IBM Corp. 2016, 2018
- *
- * 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, { useRef, useEffect } from 'react';
-import { Popover, PopoverContent } from '../../Popover';
-import { keys, match } from '../../../internal/keyboard';
-import { useDelayedState } from '../../../internal/useDelayedState';
-import { useId } from '../../../internal/useId';
-import {
- useNoInteractiveChildren,
- getInteractiveContent,
-} from '../../../internal/useNoInteractiveChildren';
-import { usePrefix } from '../../../internal/usePrefix';
-
-function Tooltip({
- align = 'top',
- className: customClassName,
- children,
- label,
- description,
- enterDelayMs = 100,
- leaveDelayMs = 300,
- defaultOpen = false,
- ...rest
-}) {
- const containerRef = useRef(null);
- const tooltipRef = useRef(null);
- const [open, setOpen] = useDelayedState(defaultOpen);
- const id = useId('tooltip');
- const prefix = usePrefix();
- const child = React.Children.only(children);
-
- const triggerProps = {
- onFocus: () => setOpen(true),
- onBlur: () => setOpen(false),
- // This should be placed on the trigger in case the element is disabled
- onMouseEnter,
- };
-
- if (label) {
- triggerProps['aria-labelledby'] = id;
- } else {
- triggerProps['aria-describedby'] = id;
- }
-
- function onKeyDown(event) {
- if (open && match(event, keys.Escape)) {
- event.stopPropagation();
- setOpen(false);
- }
- }
-
- function onMouseEnter() {
- setOpen(true, enterDelayMs);
- }
-
- function onMouseLeave() {
- setOpen(false, leaveDelayMs);
- }
-
- useNoInteractiveChildren(
- tooltipRef,
- 'The Tooltip component must have no interactive content rendered by the' +
- '`label` or `description` prop'
- );
-
- useEffect(() => {
- const interactiveContent = getInteractiveContent(containerRef.current);
- if (!interactiveContent) {
- setOpen(false);
- }
- });
-
- return (
-
- {React.cloneElement(child, triggerProps)}
-
- {label || description}
-
-
- );
-}
-
-Tooltip.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',
- ]),
-
- /**
- * Pass in the child to which the tooltip will be applied
- */
- children: PropTypes.node,
-
- /**
- * 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,
-
- /**
- * Provide the description to be rendered inside of the Tooltip. The
- * description will use `aria-describedby` and will describe the child node
- * in addition to the text rendered inside of the child. This means that if you
- * have text in the child node, that it will be announced alongside the
- * description to the screen reader.
- *
- * Note: if label and description are both provided, label will be used and
- * description will not be used
- */
- description: PropTypes.node,
-
- /**
- * Specify the duration in milliseconds to delay before displaying the tooltip
- */
- 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, that it will not be
- * announced to the screen reader.
- *
- * Note: if label and description are both provided, description will not be
- * used
- */
- label: PropTypes.node,
-
- /**
- * Specify the duration in milliseconds to delay before hiding the tooltip
- */
- leaveDelayMs: PropTypes.number,
-};
-
-export { Tooltip };
diff --git a/packages/react/src/components/Tooltip/next/Tooltip.mdx b/packages/react/src/components/Tooltip/next/Tooltip.mdx
deleted file mode 100644
index 2ede2af6519d..000000000000
--- a/packages/react/src/components/Tooltip/next/Tooltip.mdx
+++ /dev/null
@@ -1,123 +0,0 @@
-import { Story, ArgsTable, Canvas } from '@storybook/addon-docs';
-
-# Tooltip
-
-[Source code](https://github.com/carbon-design-system/carbon/tree/main/packages/react/src/components/Tooltip/next)
- |
-[Usage guidelines](https://www.carbondesignsystem.com/components/tooltip/usage)
- |
-[Accessibility](https://www.carbondesignsystem.com/components/tooltip/accessibility)
-
-
-
-
-## Table of Contents
-
-- [Overview](#overview)
- - [Toggletips vs Tooltips](#toggletips-vs-tooltips)
- - [Customizing the content of a tooltip](#customizing-the-content-of-a-tooltip)
- - [Tooltip alignment](#tooltip-alignment)
- - [Customizing the duration of a tooltip](#customizing-the-duration-of-a-tooltip)
-- [Component API](#component-api)
-- [Feedback](#feedback)
-
-
-
-## Overview
-
-You can use the `Tooltip` component as a wrapper to display a popup for an
-interactive element that you provide. If you are trying to place interactive
-content inside of the `Tooltip` itself, consider using `Toggletip` instead. You
-can provide the interactive element as a child to `Tooltip`, for example:
-
-```jsx
-
-
-
-```
-
-`Tooltip` accepts a single child for its `children` prop. It is important that
-the component you provide renders an interactive element. In addition, you can
-specify the contents of the popup through the `label` or `description` prop.
-
-
-
-### Toggletips vs Tooltips
-
-Toggletips and tooltips are similar visually and both contain a popover and
-interactive trigger element. The two components differ in the way they are
-invoked and dismissed and if the user must interact with the contents. A tooltip
-is exposed on hover or focus when you need to expose brief, supplemental
-information that is not interactive. A toggletip is used on click or enter when
-you need to expose interactive elements, such as button, that a user needs to
-interact with.
-
-### Customizing the content of a tooltip
-
-The `Tooltip` component supports tooltip customization through the `label` and
-`description` props. Under the hood, `label` will map to `aria-labelledby` and
-`description` will map to `aria-describedby`.
-
-It's important to consider the type of content that you provide through the
-tooltip in order to know whether to use `label` or `description`. If the
-information shown is the only content that describes your component, you should
-use a `label`. If the information is secondary or adds additional information,
-`description` would be a good pick.
-
-While the `label` and `description` props accept any arbitrary React element,
-it's important that the value you pass to either prop has no interactive
-content. This will cause a violation in the component as the semantics of this
-content will not be accessible to users of screen reader software.
-
-### Tooltip alignment
-
-The `align` prop allows you to specify where your content should be placed
-relative to the tooltip. For example, if you provide `align="top"` to the
-`Tooltip` component then the tooltip will render above your component.
-Similarly, if you provide `align="bottom"` then the tooltip will render below
-your component.
-
-
-
-You can also configure the placement of the caret, if it is enabled, by choosing
-between `left` and `right` or `bottom` and `top`, depending on where your
-tooltip is being rendered.
-
-If you are using `align="top"`, then you can choose between `align="top-left"`
-and `align="top-right"`. These options will place the caret closer to the left
-or right edges of the tooltip.
-
-If you are using `align="left"` or `align="right"`, you can use `top` or
-`bottom` to control this alignment.
-
-### Customizing the duration of a tooltip
-
-You can customize the delay between when a tooltip is invoked and its contents
-are shown. You can also customize the delay between when a tooltip is dismissed
-and its contents are hidden.
-
-
-
-The `enterDelayMs` prop allows you to provide a time, in milliseconds, that the
-component will wait before showing the tooltip. The `exitDelayMs` prop allows
-you to provide a time, in milliseconds, that the component will wait before
-hiding the tooltip.
-
-By default, these values are set to 100ms for `enterDelayms` and 300ms for
-`exitDelayMs`.
-
-## Component API
-
-
-
-## Feedback
-
-Help us improve this component by providing feedback, asking questions on Slack,
-or updating this file on
-[GitHub](https://github.com/carbon-design-system/carbon/edit/main/packages/react/src/components/Tooltip/next/Tooltip.mdx).
diff --git a/packages/react/src/components/Tooltip/next/index.js b/packages/react/src/components/Tooltip/next/index.js
deleted file mode 100644
index e4fd5f092da9..000000000000
--- a/packages/react/src/components/Tooltip/next/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Copyright IBM Corp. 2016, 2018
- *
- * 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 { DefinitionTooltip } from './DefinitionTooltip';
-import { Tooltip } from './Tooltip';
-
-export { DefinitionTooltip, Tooltip };
diff --git a/packages/react/src/components/Tooltip/next/story.scss b/packages/react/src/components/Tooltip/story.scss
similarity index 100%
rename from packages/react/src/components/Tooltip/next/story.scss
rename to packages/react/src/components/Tooltip/story.scss
diff --git a/packages/react/src/index.js b/packages/react/src/index.js
index 57f33606e848..9d6fab5f50f6 100644
--- a/packages/react/src/index.js
+++ b/packages/react/src/index.js
@@ -264,12 +264,12 @@ export {
export { Popover, PopoverContent } from './components/Popover';
export { default as ProgressBar } from './components/ProgressBar';
export { HStack, Stack, VStack } from './components/Stack';
-export { Tooltip } from './components/Tooltip/next';
+export { Tooltip } from './components/Tooltip';
export {
Text as unstable_Text,
TextDirection as unstable_TextDirection,
} from './components/Text';
-export { DefinitionTooltip } from './components/Tooltip/next/DefinitionTooltip';
+export { DefinitionTooltip } from './components/Tooltip/DefinitionTooltip';
export { GlobalTheme, Theme, useTheme } from './components/Theme';
export { usePrefix } from './internal/usePrefix';
export { useIdPrefix } from './internal/useIdPrefix';
diff --git a/packages/styles/scss/components/contained-list/_contained-list.scss b/packages/styles/scss/components/contained-list/_contained-list.scss
index 8d8958bf86cf..225dffdc4d1b 100644
--- a/packages/styles/scss/components/contained-list/_contained-list.scss
+++ b/packages/styles/scss/components/contained-list/_contained-list.scss
@@ -5,6 +5,8 @@
// LICENSE file in the root directory of this source tree.
//
+@use 'sass:list';
+
@use '../../config' as *;
@use '../../motion' as *;
@use '../../spacing' as *;
@@ -31,6 +33,35 @@
width: 100%;
}
+ // Sizes
+
+ $sizes: (
+ // size: (height, item-block-padding)
+ 'sm': (rem(32px), $spacing-03),
+ 'md': (rem(40px), $spacing-04),
+ 'lg': (rem(48px), $spacing-05),
+ 'xl': (rem(64px), $spacing-06)
+ );
+
+ @each $size, $definition in $sizes {
+ $height: list.nth($definition, 1);
+ $item-block-padding: list.nth($definition, 2);
+
+ .#{$prefix}--contained-list--on-page.#{$prefix}--contained-list--#{$size}
+ .#{$prefix}--contained-list__header {
+ height: $height;
+ }
+
+ .#{$prefix}--contained-list--#{$size}
+ .#{$prefix}--contained-list-item__content,
+ .#{$prefix}--contained-list--#{$size}
+ .#{$prefix}--contained-list-item--clickable
+ .#{$prefix}--contained-list-item__content {
+ min-height: $height;
+ padding: calc(#{$item-block-padding} - #{rem(2px)}) $spacing-05;
+ }
+ }
+
// "On Page" variant
.#{$prefix}--contained-list--on-page + .#{$prefix}--contained-list--on-page {
@@ -40,7 +71,6 @@
.#{$prefix}--contained-list--on-page .#{$prefix}--contained-list__header {
@include type-style('heading-compact-01');
- height: $spacing-09;
border-bottom: 1px solid $border-subtle;
background-color: $background;
color: $text-primary;
@@ -92,7 +122,6 @@
.#{$prefix}--contained-list-item__content {
@include type-style('body-01');
- padding: calc(#{$spacing-05} - #{rem(2px)}) $spacing-05;
color: $text-primary;
}
diff --git a/packages/styles/scss/components/pagination/_pagination.scss b/packages/styles/scss/components/pagination/_pagination.scss
index 43f13430ac50..5f35b2c0023b 100644
--- a/packages/styles/scss/components/pagination/_pagination.scss
+++ b/packages/styles/scss/components/pagination/_pagination.scss
@@ -134,9 +134,7 @@
border-right: 1px solid $border-subtle;
}
- .#{$prefix}--pagination
- .#{$prefix}--select__page-number
- .#{$prefix}--select-input {
+ .#{$prefix}--pagination__right {
border-left: 1px solid $border-subtle;
}
@@ -166,6 +164,16 @@
margin-left: rem(1px);
}
+ .#{$prefix}--pagination__right
+ .#{$prefix}--pagination__text.#{$prefix}--pagination__page-text {
+ margin-right: rem(1px);
+ margin-left: 1rem;
+ }
+
+ .#{$prefix}--pagination__right .#{$prefix}--pagination__text:empty {
+ margin: 0;
+ }
+
.#{$prefix}--pagination__left {
padding: 0 $spacing-05 0 0;