Skip to content

Commit e16818a

Browse files
authored
feat(modals, tooltips): adds fallbackPlacements support to Tooltip and TooltipModal (#2020)
1 parent 0101f3c commit e16818a

File tree

8 files changed

+44
-16
lines changed

8 files changed

+44
-16
lines changed

packages/modals/demo/stories/TooltipDialogStory.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const TooltipDialogStory: Story<IArgs> = ({
9090
{!!hasClose && <TooltipDialog.Close aria-label={closeAriaLabel} />}
9191
</TooltipDialog>
9292
<Grid>
93-
<Grid.Row style={{ height: 'calc(100vh - 80px)' }}>
93+
<Grid.Row style={{ height: 'calc(100vh - 200px)' }}>
9494
{[...Array(count)].map((_, index) => (
9595
<Grid.Col key={index} md={4} textAlign="center" alignSelf="center">
9696
<IconButton

packages/modals/demo/tooltipDialog.stories.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useArgs } from '@storybook/client-api';
33
import { TooltipDialog } from '@zendeskgarden/react-modals';
44
import { TooltipDialogStory } from './stories/TooltipDialogStory';
55
import { TOOLTIP_DIALOG_BODY as BODY } from './stories/data';
6+
import { PLACEMENT } from '../src/types';
67
import README from '../README.md';
78

89
<Meta
@@ -41,6 +42,7 @@ import README from '../README.md';
4142
}}
4243
argTypes={{
4344
referenceElement: { control: false },
45+
fallbackPlacements: { control: 'multi-select', options: PLACEMENT.filter(p => p !== 'auto') },
4446
hasBody: { name: 'TooltipDialog.Body', table: { category: 'Story' } },
4547
hasClose: { name: 'TooltipDialog.Close', table: { category: 'Story' } },
4648
hasFooter: { name: 'TooltipDialog.Footer', table: { category: 'Story' } },

packages/modals/src/elements/TooltipDialog/TooltipDialog.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ import React, { HTMLAttributes, useState, useContext, useEffect, useRef } from '
99
import PropTypes from 'prop-types';
1010
import { ThemeContext } from 'styled-components';
1111
import { CSSTransition } from 'react-transition-group';
12-
import { autoPlacement, autoUpdate, offset, platform, useFloating } from '@floating-ui/react-dom';
12+
import {
13+
autoPlacement,
14+
autoUpdate,
15+
flip,
16+
offset,
17+
platform,
18+
useFloating
19+
} from '@floating-ui/react-dom';
1320
import { useModal } from '@zendeskgarden/container-modal';
1421
import { mergeRefs } from 'react-merge-refs';
1522
import { TooltipDialogContext } from '../../utils/useTooltipDialogContext';
@@ -18,7 +25,7 @@ import {
1825
StyledTooltipDialog,
1926
StyledTooltipDialogBackdrop
2027
} from '../../styled';
21-
import { ITooltipDialogProps } from '../../types';
28+
import { ITooltipDialogProps, PLACEMENT } from '../../types';
2229
import { Title } from './Title';
2330
import { Body } from './Body';
2431
import { Close } from './Close';
@@ -35,6 +42,7 @@ const TooltipDialogComponent = React.forwardRef<HTMLDivElement, ITooltipDialogPr
3542
appendToNode,
3643
referenceElement,
3744
placement: _placement,
45+
fallbackPlacements: _fallbackPlacements,
3846
offset: _offset,
3947
onClose,
4048
hasArrow,
@@ -64,9 +72,10 @@ const TooltipDialogComponent = React.forwardRef<HTMLDivElement, ITooltipDialogPr
6472
restoreFocus: false
6573
});
6674

67-
const [floatingPlacement] = getFloatingPlacements(
75+
const [floatingPlacement, fallbackPlacements] = getFloatingPlacements(
6876
theme,
69-
_placement === 'auto' ? PLACEMENT_DEFAULT : _placement!
77+
_placement === 'auto' ? PLACEMENT_DEFAULT : _placement!,
78+
_fallbackPlacements
7079
);
7180

7281
const {
@@ -83,7 +92,7 @@ const TooltipDialogComponent = React.forwardRef<HTMLDivElement, ITooltipDialogPr
8392
placement: floatingPlacement,
8493
middleware: [
8594
offset(_offset === undefined ? theme.space.base * 3 : _offset),
86-
_placement === 'auto' ? autoPlacement() : undefined
95+
_placement === 'auto' ? autoPlacement() : flip({ fallbackPlacements })
8796
]
8897
});
8998

@@ -194,6 +203,10 @@ TooltipDialogComponent.propTypes = {
194203
appendToNode: PropTypes.any,
195204
referenceElement: PropTypes.any,
196205
placement: PropTypes.any,
206+
// @ts-expect-error Validation error due to incorrect type inference when component is wrapped in forwardRef
207+
fallbackPlacements: PropTypes.arrayOf(
208+
PropTypes.oneOf(PLACEMENT.filter(placement => placement !== 'auto'))
209+
),
197210
isAnimated: PropTypes.bool,
198211
hasArrow: PropTypes.bool,
199212
zIndex: PropTypes.number,

packages/modals/src/types/index.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,23 @@ export interface IDrawerHeaderProps extends HTMLAttributes<HTMLDivElement> {
7676

7777
export interface ITooltipDialogProps extends Omit<IModalProps, 'isCentered' | 'isLarge'> {
7878
/**
79-
* Positions the modal relative to the provided `HTMLElement`
79+
* Provides a list of acceptable fallback placements
8080
*/
81-
referenceElement?: HTMLElement | null;
81+
fallbackPlacements?: Exclude<Placement, 'auto'>[];
82+
/**
83+
* Adds an arrow to the tooltop
84+
*/
85+
hasArrow?: boolean;
8286
/** @ignore Modifies the placement offset from the reference element (internal only) */
8387
offset?: number;
8488
/**
8589
* Adjusts the placement of the tooltip
8690
**/
8791
placement?: Placement;
8892
/**
89-
* Adds an arrow to the tooltop
93+
* Positions the modal relative to the provided `HTMLElement`
9094
*/
91-
hasArrow?: boolean;
95+
referenceElement?: HTMLElement | null;
9296
/**
9397
* Sets the `z-index` of the tooltip
9498
*/

packages/tooltips/demo/stories/TooltipStory.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ interface IArgs extends Omit<ITooltipProps, 'content'> {
1818

1919
export const TooltipStory: StoryFn<IArgs> = ({ content, ...args }: IArgs) => (
2020
<Grid>
21-
<Grid.Row style={{ height: 'calc(100vh - 80px)' }}>
21+
<Grid.Row style={{ height: 'calc(100vh - 200px)' }}>
2222
<Grid.Col textAlign="center" alignSelf="center">
2323
<Tooltip
2424
{...args}

packages/tooltips/demo/tooltip.stories.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Tooltip, Title, Paragraph } from '@zendeskgarden/react-tooltips';
33
import { TooltipStory } from './stories/TooltipStory';
44
import { TOOLTIP_CONTENT as CONTENT } from './stories/data';
55
import README from '../README.md';
6+
import { PLACEMENT } from '../src/types';
67

78
<Meta
89
title="Packages/Tooltips/Tooltip"
@@ -29,7 +30,8 @@ import README from '../README.md';
2930
}}
3031
argTypes={{
3132
isVisible: { control: 'boolean' },
32-
appendToNode: { control: false }
33+
appendToNode: { control: false },
34+
fallbackPlacements: { control: 'multi-select', options: PLACEMENT.filter(p => p !== 'auto') }
3335
}}
3436
parameters={{
3537
design: {

packages/tooltips/src/elements/Tooltip.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useTooltip } from '@zendeskgarden/container-tooltip';
1414
import { composeEventHandlers, getControlledValue } from '@zendeskgarden/container-utilities';
1515
import { StyledTooltipWrapper, StyledTooltip } from '../styled';
1616
import { ITooltipProps, PLACEMENT, SIZE, TYPE } from '../types';
17-
import { autoPlacement, autoUpdate, platform, useFloating } from '@floating-ui/react-dom';
17+
import { autoPlacement, autoUpdate, flip, platform, useFloating } from '@floating-ui/react-dom';
1818
import { DEFAULT_THEME, getFloatingPlacements } from '@zendeskgarden/react-theming';
1919
import { toSize } from './utils';
2020
import { Paragraph } from './Paragraph';
@@ -29,6 +29,7 @@ export const TooltipComponent = ({
2929
content,
3030
refKey,
3131
placement: _placement,
32+
fallbackPlacements: _fallbackPlacements,
3233
children,
3334
hasArrow,
3435
size,
@@ -51,9 +52,10 @@ export const TooltipComponent = ({
5152
});
5253

5354
const controlledIsVisible = getControlledValue(externalIsVisible, isVisible);
54-
const [floatingPlacement] = getFloatingPlacements(
55+
const [floatingPlacement, fallbackPlacements] = getFloatingPlacements(
5556
theme,
56-
_placement === 'auto' ? PLACEMENT_DEFAULT : _placement!
57+
_placement === 'auto' ? PLACEMENT_DEFAULT : _placement!,
58+
_fallbackPlacements
5759
);
5860

5961
const {
@@ -68,7 +70,7 @@ export const TooltipComponent = ({
6870
},
6971
elements: { reference: triggerRef?.current, floating: floatingRef?.current },
7072
placement: floatingPlacement,
71-
middleware: _placement === 'auto' ? [autoPlacement()] : undefined
73+
middleware: _placement === 'auto' ? [autoPlacement()] : [flip({ fallbackPlacements })]
7274
});
7375

7476
useEffect(() => {
@@ -135,6 +137,9 @@ TooltipComponent.propTypes = {
135137
id: PropTypes.string,
136138
content: PropTypes.node.isRequired,
137139
placement: PropTypes.oneOf(PLACEMENT),
140+
fallbackPlacements: PropTypes.arrayOf(
141+
PropTypes.oneOf(PLACEMENT.filter(placement => placement !== 'auto'))
142+
),
138143
size: PropTypes.oneOf(SIZE),
139144
type: PropTypes.oneOf(TYPE),
140145
zIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

packages/tooltips/src/types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export interface ITooltipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'con
2525
delayMS?: number;
2626
/** Defines the content of the tooltip */
2727
content: ReactNode;
28+
/** Provides a list of acceptable fallback placements */
29+
fallbackPlacements?: Exclude<GardenPlacement, 'auto'>[];
2830
/** Adjusts the placement of the tooltip */
2931
placement?: GardenPlacement;
3032
/** Adjusts the padding and font size */

0 commit comments

Comments
 (0)