diff --git a/packages/react-components/react-tooltip/stories/src/Tooltip/TooltipWithOverflow.stories.tsx b/packages/react-components/react-tooltip/stories/src/Tooltip/TooltipWithOverflow.stories.tsx new file mode 100644 index 00000000000000..af26b84dfc4cf3 --- /dev/null +++ b/packages/react-components/react-tooltip/stories/src/Tooltip/TooltipWithOverflow.stories.tsx @@ -0,0 +1,170 @@ +import * as React from 'react'; +import { + Tooltip, + Text, + tokens, + mergeClasses, + useIsomorphicLayoutEffect, + useFluent, + makeStyles, + Switch, +} from '@fluentui/react-components'; + +const useStyles = makeStyles({ + wrapper: { + display: 'flex', + flexDirection: 'column', + padding: '20px', + gap: '30px', + }, + overflowTextWrapper: { + border: `2px dashed ${tokens.colorNeutralStroke1}`, + }, + reducedWrapper: { + maxWidth: '300px', + border: `2px dashed ${tokens.colorStatusDangerBorder2}`, + }, + overflowContainer: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, + resizableArea: { + minWidth: '200px', + maxWidth: '800px', + border: `2px solid ${tokens.colorBrandBackground}`, + padding: '20px 10px 10px 10px', + position: 'relative', + resize: 'horizontal', + '::after': { + content: `'Resizable Area'`, + position: 'absolute', + padding: '1px 4px 1px', + top: '-2px', + left: '-2px', + fontFamily: 'monospace', + fontSize: '15px', + fontWeight: 900, + lineHeight: 1, + letterSpacing: '1px', + color: tokens.colorNeutralForegroundOnBrand, + backgroundColor: tokens.colorBrandBackground, + }, + }, +}); + +type Overflow = { + x: boolean; + y: boolean; +}; + +const useIsOverflow = (ref: React.RefObject, callback?: (overflow: Overflow) => void) => { + const { targetDocument } = useFluent(); + const [overflow, setOverflow] = React.useState({ x: false, y: false }); + + const observer = React.useRef(null); + + useIsomorphicLayoutEffect(() => { + const { current } = ref; + + if (!current || !targetDocument) { + return; + } + + const trigger = () => { + const overflowX = current.scrollWidth > current.clientWidth; + const overflowY = current.scrollHeight > current.clientHeight; + + const newOverflow = { x: overflowX, y: overflowY }; + + setOverflow(prev => { + if (prev.x !== newOverflow.x || prev.y !== newOverflow.y) { + if (callback) { + callback(newOverflow); + } + + return newOverflow; + } + return prev; + }); + }; + + if (targetDocument.defaultView && 'ResizeObserver' in targetDocument.defaultView) { + observer.current = new ResizeObserver(trigger); + observer.current.observe(current); + } + + trigger(); + + return () => { + observer.current?.disconnect(); + }; + }, [ref, callback, targetDocument]); + + return overflow; +}; + +export const WithOverflow = () => { + const styles = useStyles(); + const parentRef = React.useRef(null); + const textRef = React.useRef(null); + + const [isForced, setIsForced] = React.useState(false); + const [isVisibleFirst, setIsVisibleFirst] = React.useState(false); + const [isVisibleSecond, setIsVisibleSecond] = React.useState(false); + + const { x: overflowXParent } = useIsOverflow(parentRef); + const { x: overflowXText } = useIsOverflow(textRef); + + const handleForceOverflow = React.useCallback(ev => { + setIsForced(ev.currentTarget.checked); + }, []); + + return ( +
+ +
+ { + setIsVisibleFirst(data.visible); + }} + content="Tooltip content" + > + + If the parent element's content overflows, hovering here will show a tooltip (anchored to the parent + element). + + +
+
+ { + setIsVisibleSecond(data.visible); + }} + content="Tooltip content" + > + + If the Tooltip's content overflows, hovering here will show a tooltip. + + +
+
+ ); +}; + +WithOverflow.parameters = { + docs: { + description: { + story: 'Tooltip can be controlled and shown only based on overflow condition', + }, + }, +}; diff --git a/packages/react-components/react-tooltip/stories/src/Tooltip/index.stories.tsx b/packages/react-components/react-tooltip/stories/src/Tooltip/index.stories.tsx index b8b7caaa06bdd8..d422902b38e11c 100644 --- a/packages/react-components/react-tooltip/stories/src/Tooltip/index.stories.tsx +++ b/packages/react-components/react-tooltip/stories/src/Tooltip/index.stories.tsx @@ -13,6 +13,7 @@ export { Controlled } from './TooltipControlled.stories'; export { Positioning } from './TooltipPositioning.stories'; export { Target } from './TooltipTarget.stories'; export { Icon } from './TooltipIcon.stories'; +export { WithOverflow } from './TooltipWithOverflow.stories'; export default { title: 'Components/Tooltip',