Skip to content

Commit 4ea4f00

Browse files
authored
[LG-5008] fix(popover): improve positioning logic with enhanced fallback behavior and improve guide cue alignment (#3226)
* chore: clean up GuideCue and Tooltip stories * fix(guide-cue): beacon positioning * fix(popover): fallback position, consider adjustOnMutation, and hide if reference hidden * fix(popover): rm hide middleware for now * chore: changeset * test(popover): add story showing adjustOnMutation behavior and update docs * refactor(popover): revert consideration of adjustOnMutation prop * fix(date-picker): flaky spec * refactor(guide-cue): rename TooltipContent to GuideCueTooltip
1 parent 62ddea8 commit 4ea4f00

File tree

11 files changed

+78
-57
lines changed

11 files changed

+78
-57
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@leafygreen-ui/guide-cue': minor
3+
'@leafygreen-ui/popover': minor
4+
---
5+
6+
[LG-5008](https://jira.mongodb.org/browse/LG-5008)
7+
8+
- Improves positioning logic with enhanced positioning fallback to optimize popover element visibility
9+
- Assigns `GuideCue` component's internal beacon ref for better positioning of tooltip instances relative to beacon

packages/date-picker/src/DatePicker/DatePicker.keyboard2.spec.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { waitFor, waitForElementToBeRemoved } from '@testing-library/react';
1+
import {
2+
act,
3+
waitFor,
4+
waitForElementToBeRemoved,
5+
} from '@testing-library/react';
26
import userEvent from '@testing-library/user-event';
37

48
import { Month, newUTC } from '@leafygreen-ui/date-utils';
@@ -183,30 +187,25 @@ describe('DatePicker keyboard interaction', () => {
183187
});
184188

185189
test('does not close the main menu if a select menu is open', async () => {
186-
const { openMenu, queryAllByRole, findAllByRole } = renderDatePicker();
190+
const { openMenu, findAllByRole } = renderDatePicker();
187191
const { monthSelect, menuContainerEl } = await openMenu();
188192

189193
tabNTimes(3);
190194
expect(monthSelect).toHaveFocus();
191195

192196
userEvent.keyboard('[Enter]');
193-
await waitFor(() => {
194-
jest.advanceTimersByTime(transitionDuration.default);
195-
});
197+
act(() => jest.advanceTimersByTime(transitionDuration.default));
196198

197199
const options = await findAllByRole('option');
198200
const firstOption = options[0];
199201
userEvent.keyboard('{arrowdown}');
200202
expect(firstOption).toHaveFocus();
201203

202-
const listBoxes = queryAllByRole('listbox');
203-
expect(listBoxes).toHaveLength(2);
204-
205-
const selectMenu = listBoxes[1];
206204
userEvent.keyboard('{escape}');
207-
await waitForElementToBeRemoved(selectMenu);
208-
expect(menuContainerEl).toBeInTheDocument();
209-
expect(monthSelect).toHaveFocus();
205+
act(() => jest.advanceTimersByTime(transitionDuration.default));
206+
207+
expect(menuContainerEl).toBeVisible();
208+
await waitFor(() => expect(monthSelect).toHaveFocus());
210209
});
211210
});
212211

packages/guide-cue/src/GuideCue.stories.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,23 @@ const meta: StoryMetaType<any> = {
108108
buttonText: {
109109
control: 'text',
110110
},
111+
tooltipAlign: {
112+
control: { type: 'radio' },
113+
options: Object.values(TooltipAlign),
114+
},
115+
tooltipJustify: {
116+
control: { type: 'radio' },
117+
options: Object.values(TooltipJustify),
118+
},
111119
},
112120
args: {
113121
title: 'New feature',
114122
children: 'This is a new feature. You should try it out',
115123
buttonText: 'Next',
116124
numberOfSteps: 4,
117125
currentStep: 2,
126+
tooltipAlign: TooltipAlign.Top,
127+
tooltipJustify: TooltipJustify.Middle,
118128
},
119129
};
120130

packages/guide-cue/src/GuideCue/GuideCue.tsx

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Popover, {
1010
RenderMode,
1111
} from '@leafygreen-ui/popover';
1212

13-
import TooltipContent from '../TooltipContent';
13+
import GuideCueTooltip from '../GuideCueTooltip';
1414

1515
import { beaconStyles, timeout1, timeout2 } from './GuideCue.styles';
1616
import { GuideCueProps, TooltipAlign, TooltipJustify } from './GuideCue.types';
@@ -140,37 +140,36 @@ function GuideCue({
140140
{isStandalone ? (
141141
// Standalone tooltip
142142
// this is using the reference from the `refEl` prop to position itself against
143-
<TooltipContent {...tooltipContentProps}>{children}</TooltipContent>
143+
<GuideCueTooltip {...tooltipContentProps}>{children}</GuideCueTooltip>
144144
) : (
145145
// Multistep tooltip
146-
<Popover
147-
active={popoverOpen}
148-
refEl={refEl}
149-
align={beaconAlign}
150-
justify={TooltipJustify.Middle}
151-
spacing={-12} // width of beacon is 24px, 24/2 = 12
152-
adjustOnMutation={true}
153-
dismissMode={DismissMode.Manual}
154-
renderMode={RenderMode.TopLayer}
155-
>
156-
{/* The beacon is using the popover component to position itself */}
157-
<div
146+
<>
147+
<Popover
148+
active={popoverOpen}
149+
refEl={refEl}
150+
align={beaconAlign}
151+
justify={TooltipJustify.Middle}
152+
spacing={-12} // width of beacon is 24px, 24/2 = 12
153+
adjustOnMutation={true}
154+
dismissMode={DismissMode.Manual}
155+
renderMode={RenderMode.TopLayer}
158156
ref={beaconRef}
159-
className={beaconStyles(prefersReducedMotion, darkMode)}
160157
>
161-
<div />
162-
</div>
163-
158+
{/* The beacon is using the popover component to position itself */}
159+
<div className={beaconStyles(prefersReducedMotion, darkMode)}>
160+
<div />
161+
</div>
162+
</Popover>
164163
{/* The tooltip is using the ref of the beacon as the trigger to position itself against */}
165164
{/* Instead of passing the beacon as the tooltip trigger prop we pass a reference to the beacon to the `refEl` prop. By passing only the reference we avoid default tooltip behaviors such as closing the tooltip on background click or showing and hiding the tooltip on hover. */}
166-
<TooltipContent
165+
<GuideCueTooltip
167166
{...tooltipContentProps}
168167
refEl={beaconRef}
169168
open={tooltipOpen}
170169
>
171170
{children}
172-
</TooltipContent>
173-
</Popover>
171+
</GuideCueTooltip>
172+
</>
174173
)}
175174
</>
176175
);

packages/guide-cue/src/TooltipContent/TooltipContent.tsx renamed to packages/guide-cue/src/GuideCueTooltip/GuideCueTooltip.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
stepStyles,
2525
tooltipMultistepStyles,
2626
tooltipStyles,
27-
} from './TooltipContent.styles';
27+
} from './GuideCueTooltip.styles';
2828

2929
const ariaLabelledby = 'guide-cue-label';
3030
const ariaDescribedby = 'guide-cue-desc';
@@ -47,7 +47,7 @@ const focusTrapOptions: Options = {
4747
},
4848
};
4949

50-
type TooltipContentProps = Partial<GuideCueProps> & {
50+
type GuideCueTooltipProps = Partial<GuideCueProps> & {
5151
theme: Theme;
5252
title: string;
5353
isStandalone: boolean;
@@ -57,7 +57,7 @@ type TooltipContentProps = Partial<GuideCueProps> & {
5757
handleCloseClick: () => void;
5858
};
5959

60-
function TooltipContent({
60+
function GuideCueTooltip({
6161
darkMode,
6262
open,
6363
setOpen,
@@ -76,7 +76,7 @@ function TooltipContent({
7676
handleButtonClick,
7777
handleCloseClick,
7878
...tooltipProps
79-
}: TooltipContentProps) {
79+
}: GuideCueTooltipProps) {
8080
const focusId = useIdAllocator({ prefix: 'guide-cue' });
8181

8282
return (
@@ -151,5 +151,5 @@ function TooltipContent({
151151
);
152152
}
153153

154-
TooltipContent.displayName = 'TooltipContent';
155-
export default TooltipContent;
154+
GuideCueTooltip.displayName = 'GuideCueTooltip';
155+
export default GuideCueTooltip;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './GuideCueTooltip';

packages/guide-cue/src/TooltipContent/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/popover/src/Popover.stories.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { StoryFn, StoryObj } from '@storybook/react';
88
import Button from '@leafygreen-ui/button';
99
import { css } from '@leafygreen-ui/emotion';
1010
import { palette } from '@leafygreen-ui/palette';
11-
import { color } from '@leafygreen-ui/tokens';
11+
import { color, spacing } from '@leafygreen-ui/tokens';
1212

1313
import { getPopoverRenderModeProps } from './utils/getPopoverRenderModeProps';
1414
import {
@@ -21,10 +21,10 @@ import {
2121
ToggleEvent,
2222
} from './Popover';
2323

24-
const popoverStyle = css`
24+
const popoverStyles = css`
2525
border: 1px solid ${palette.gray.light1};
2626
text-align: center;
27-
padding: 12px;
27+
padding: ${spacing[300]}px;
2828
max-height: 100%;
2929
overflow: hidden;
3030
// Reset these properties since they'll be inherited
@@ -91,7 +91,7 @@ const meta: StoryMetaType<typeof Popover> = {
9191
},
9292
args: {
9393
active: true,
94-
children: <div className={popoverStyle}>Popover content</div>,
94+
children: <div className={popoverStyles}>Popover content</div>,
9595
},
9696

9797
decorator: Instance => {
@@ -211,7 +211,7 @@ export const LiveExample: StoryFn<PopoverStoryProps> = ({
211211
{buttonText}
212212
</Button>
213213
<Popover {...popoverProps}>
214-
<div className={popoverStyle}>Popover content</div>
214+
<div className={popoverStyles}>Popover content</div>
215215
</Popover>
216216
</div>
217217
);
@@ -244,7 +244,7 @@ const PortalPopoverInScrollableContainer = ({
244244
portalRef={portalRef}
245245
scrollContainer={scrollContainer.current}
246246
>
247-
<div className={popoverStyle}>Popover content</div>
247+
<div className={popoverStyles}>Popover content</div>
248248
</Popover>
249249
</Button>
250250
</div>
@@ -295,7 +295,7 @@ const InlinePopover = ({ buttonText, ...props }: PopoverStoryProps) => {
295295
refEl={buttonRef}
296296
renderMode={RenderMode.Inline}
297297
>
298-
<div className={popoverStyle}>Popover content</div>
298+
<div className={popoverStyles}>Popover content</div>
299299
</Popover>
300300
</div>
301301
);

packages/popover/src/Popover/Popover.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,14 @@ export const Popover = forwardRef<HTMLDivElement, PopoverComponentProps>(
134134
),
135135
flip({
136136
boundary: scrollContainer ?? 'clippingAncestors',
137+
mainAxis: true,
138+
crossAxis: true,
139+
fallbackAxisSideDirection: 'start',
137140
}),
138141
],
139142
open: active,
140143
placement: getFloatingPlacement(align, justify),
141-
strategy: renderMode === RenderMode.TopLayer ? 'fixed' : 'absolute',
144+
strategy: 'absolute',
142145
transform: false,
143146
whileElementsMounted: autoUpdate,
144147
});

0 commit comments

Comments
 (0)