Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Popup)!: migrate to floating-ui #1952

Merged
merged 4 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(Popup)!: fix review comments
  • Loading branch information
oynikishin committed Nov 26, 2024
commit c8ff66a2723146a325b65812b1187c50048f8425
2 changes: 1 addition & 1 deletion src/components/ActionTooltip/ActionTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function ActionTooltip(props: ActionTooltipProps) {
style={style}
open={tooltipVisible && !disabled}
placement={placement}
anchorEl={anchorElement}
anchorElement={anchorElement}
disableEscapeKeyDown
disableOutsideClick
disableLayer
Expand Down
1 change: 1 addition & 0 deletions src/components/DropdownMenu/DropdownMenuPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export const DropdownMenuPopup = <T,>({
open={open}
anchorRef={anchorRef}
onClose={onClose}
placement="bottom-start"
{...popupProps}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
Expand Down
6 changes: 3 additions & 3 deletions src/components/Popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const Popover = React.forwardRef<PopoverInstanceProps, PopoverProps & QAP
onCloseClick,
onClick,
anchorRef,
anchorEl,
anchorElement,
strategy,
qa,
disablePortal = false,
Expand Down Expand Up @@ -117,7 +117,7 @@ export const Popover = React.forwardRef<PopoverInstanceProps, PopoverProps & QAP

const hasTitle = Boolean(title);

const hasAnchor = Boolean(anchorRef || anchorEl);
const hasAnchor = Boolean(anchorRef || anchorElement);

const popoverTitleId = `popover-${tooltipId ?? ''}-title-${useUniqId()}`;

Expand All @@ -126,7 +126,7 @@ export const Popover = React.forwardRef<PopoverInstanceProps, PopoverProps & QAP
id={tooltipId}
role={openOnHover ? 'tooltip' : 'dialog'}
strategy={strategy}
anchorEl={anchorEl}
anchorElement={anchorElement}
anchorRef={anchorRef || controlRef}
className={cnPopover(
'tooltip',
Expand Down
2 changes: 1 addition & 1 deletion src/components/Popover/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ const close = () => {

| Name | Description | Type | Default |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------------------------------------------: | :-------------------: |
| anchorEl | `Popup` anchor element. Can also be `VirtualElement`. | [`PopupAnchorElement`](../Popup/README.md#anchor) | |
| anchorElement | `Popup` anchor element. Can also be `VirtualElement`. | [`PopupAnchorElement`](../Popup/README.md#anchor) | |
| autoclosable | Whether the tooltip automatically closes when cursor moves outside it | `boolean` | `true` |
| autoFocus | If true, focus will be transferred to the first element when the popover opens | `boolean` | |
| behavior | Tooltip open/close behaviour when `openOnHover`. `"immediate"` - without any delay. `"delayed"` - with 300ms delay for opening and closing. `"delayedClosing"` - with 300ms delay only for closing. Won't be applied if `delayOpening` or `delayClosing` are passed. | `"immediate"` `"delayed"` `"delayedClosing"` | `"delayed"` |
Expand Down
2 changes: 1 addition & 1 deletion src/components/Popover/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export type PopoverDefaultProps = {

export type PopoverProps = Pick<
PopupProps,
'anchorEl' | 'anchorRef' | 'strategy' | 'placement' | 'middlewares'
'anchorElement' | 'anchorRef' | 'strategy' | 'placement' | 'middlewares'
> &
PopoverExternalProps &
PopoverBehaviorProps &
Expand Down
23 changes: 10 additions & 13 deletions src/components/Popup/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,9 @@ import type {LayerExtendableProps} from '../utils/layer-manager/LayerManager';
import {getCSSTransitionClassNames} from '../utils/transition';

import {PopupArrow} from './PopupArrow';
import {AUTO_PLACEMENTS} from './constants';
import {useAnchor} from './hooks';
import type {PopupAnchorElement, PopupAnchorRef, PopupOffset, PopupPlacement} from './types';
import {getOffsetValue} from './utils';
import {getOffsetValue, isAutoPlacement} from './utils';

import './Popup.scss';

Expand All @@ -55,7 +54,7 @@ export interface PopupProps extends DOMProps, LayerExtendableProps, QAProps {
/** floating element offset relative to anchor */
offset?: PopupOffset;
/** floating element anchor */
anchorEl?: PopupAnchorElement | null;
anchorElement?: PopupAnchorElement | null;
/** floating element anchor ref object */
anchorRef?: PopupAnchorRef;
/** Floating UI middlewares. If set, they will completely overwrite the default middlewares. */
Expand Down Expand Up @@ -119,7 +118,7 @@ export function Popup({
strategy,
placement = 'top',
offset = 4,
anchorEl,
anchorElement,
anchorRef,
floatingContext,
floatingProps,
Expand Down Expand Up @@ -159,19 +158,19 @@ export function Popup({
const containerRef = React.useRef<HTMLDivElement>(null);
const [arrowElement, setArrowElement] = React.useState<HTMLElement | null>(null);

const anchor = useAnchor(anchorEl, anchorRef);
const anchor = useAnchor(anchorElement, anchorRef);
const offsetValue = getOffsetValue(offset, hasArrow);

let placementValue: Placement | undefined;
let preventOverflowMiddleware: Middleware;

if (placement instanceof Array) {
if (Array.isArray(placement)) {
placementValue = placement[0];
preventOverflowMiddleware = flip({
altBoundary: disablePortal,
fallbackPlacements: placement.slice(1),
});
} else if ((AUTO_PLACEMENTS as readonly string[]).indexOf(placement) > -1) {
} else if (isAutoPlacement(placement)) {
let alignment: Alignment | undefined;
if (placement === 'auto-start') {
alignment = 'start';
Expand All @@ -185,7 +184,7 @@ export function Popup({
alignment,
});
} else {
placementValue = placement as Placement;
placementValue = placement;
preventOverflowMiddleware = flip({
altBoundary: disablePortal,
});
Expand All @@ -209,7 +208,7 @@ export function Popup({
placement: actualPlacement,
middlewareData,
} = useFloating({
floatingContext,
rootContext: floatingContext,
strategy,
placement: placementValue,
open,
Expand All @@ -230,10 +229,8 @@ export function Popup({

if (hasArrow && middlewareData.arrow) {
const {x, y} = middlewareData.arrow;
Object.assign(arrowStyles, {
left: x === undefined ? '' : `${x}px`,
top: y === undefined ? '' : `${y}px`,
});
arrowStyles.left = x;
arrowStyles.top = y;
}

const handleRef = useForkRef<HTMLDivElement>(
Expand Down
32 changes: 16 additions & 16 deletions src/components/Popup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The `Popup` child components are rendered inside the [`Portal`](../Portal) compo

## Anchor

To specify the anchor of a floating element, you can use either the `anchorEl` property.
To specify the anchor of a floating element, you can use either the `anchorElement` property.

<!--LANDING_BLOCK

Expand All @@ -26,7 +26,7 @@ const [open, setOpen] = React.useState(false);
<Button ref={buttonRef} onClick={() => setOpen((prevOpen) => !prevOpen)}>
Toggle Popup
</Button>
<Popup anchorEl={buttonRef.current} open={open} placement="bottom">
<Popup anchorElement={buttonRef.current} open={open} placement="bottom">
Content
</Popup>
`}>
Expand All @@ -44,7 +44,7 @@ const [open, setOpen] = React.useState(false);
<Button ref={buttonRef} onClick={() => setOpen((prevOpen) => !prevOpen)}>
Toggle Popup
</Button>
<Popup anchorEl={buttonRef.current} open={open} placement="bottom">
<Popup anchorElement={buttonRef.current} open={open} placement="bottom">
Content
</Popup>
```
Expand All @@ -65,18 +65,18 @@ It is also acceptable to use the values `auto`, `auto-start`, `auto-end` to use
const boxRef = React.useRef(null);

<div ref={boxRef} />
<Popup open anchorEl={boxRef.current} placement="top-start">Top Start</Popup>
<Popup open anchorEl={boxRef.current} placement="top">Top</Popup>
<Popup open anchorEl={boxRef.current} placement="top-end">Top End</Popup>
<Popup open anchorEl={boxRef.current} placement="right-start">Right Start</Popup>
<Popup open anchorEl={boxRef.current} placement="right">Right</Popup>
<Popup open anchorEl={boxRef.current} placement="right-end">Right End</Popup>
<Popup open anchorEl={boxRef.current} placement="bottom-end">Bottom End</Popup>
<Popup open anchorEl={boxRef.current} placement="bottom">Bottom</Popup>
<Popup open anchorEl={boxRef.current} placement="bottom-start">Bottom Start</Popup>
<Popup open anchorEl={boxRef.current} placement="left-end">Left End</Popup>
<Popup open anchorEl={boxRef.current} placement="left">Left</Popup>
<Popup open anchorEl={boxRef.current} placement="left-start">Left Start</Popup>
<Popup open anchorElement={boxRef.current} placement="top-start">Top Start</Popup>
<Popup open anchorElement={boxRef.current} placement="top">Top</Popup>
<Popup open anchorElement={boxRef.current} placement="top-end">Top End</Popup>
<Popup open anchorElement={boxRef.current} placement="right-start">Right Start</Popup>
<Popup open anchorElement={boxRef.current} placement="right">Right</Popup>
<Popup open anchorElement={boxRef.current} placement="right-end">Right End</Popup>
<Popup open anchorElement={boxRef.current} placement="bottom-end">Bottom End</Popup>
<Popup open anchorElement={boxRef.current} placement="bottom">Bottom</Popup>
<Popup open anchorElement={boxRef.current} placement="bottom-start">Bottom Start</Popup>
<Popup open anchorElement={boxRef.current} placement="left-end">Left End</Popup>
<Popup open anchorElement={boxRef.current} placement="left">Left</Popup>
<Popup open anchorElement={boxRef.current} placement="left-start">Left Start</Popup>
`}>
<UIKitExamples.PopupPlacementExample/>
</ExampleBlock>
Expand All @@ -87,7 +87,7 @@ LANDING_BLOCK-->

| Name | Description | Type | Default |
| :------------------- | :----------------------------------------------------------------------------------------- | :-----------------------------------------------------------: | :------------------------------: |
| anchorEl | Anchor element. Can also be `VirtualElement` | `PopupAnchorElement` | |
| anchorElement | Anchor element. Can also be `VirtualElement` | `PopupAnchorElement` | |
| autoFocus | While open, the focus will be set to the first interactive element in the content | `boolean` | `false` |
| children | Any React content | `React.ReactNode` | |
| className | HTML `class` attribute for root node | `string` | |
Expand Down
2 changes: 1 addition & 1 deletion src/components/Popup/__stories__/Popup.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const Default: Story = {
<Popup
{...props}
open={open}
anchorEl={anchorRef.current}
anchorElement={anchorRef.current}
onClose={() => setOpen(false)}
>
<div style={{padding: 10}}>Popup content</div>
Expand Down
12 changes: 6 additions & 6 deletions src/components/Popup/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import React from 'react';
import type {PopupAnchorElement, PopupAnchorRef} from './types';

export function useAnchor(
anchorEl: PopupAnchorElement | null | undefined,
anchorElement: PopupAnchorElement | null | undefined,
anchorRef: PopupAnchorRef | undefined,
): {
element: PopupAnchorElement | null | undefined;
ref: PopupAnchorRef | undefined;
} {
const anchorElementRef = React.useRef(anchorEl ?? null);
const anchorElementRef = React.useRef(anchorElement ?? null);
React.useEffect(() => {
anchorElementRef.current = anchorEl ?? null;
}, [anchorEl]);
anchorElementRef.current = anchorElement ?? null;
}, [anchorElement]);

if (anchorEl !== undefined) {
return {element: anchorEl, ref: anchorElementRef};
if (anchorElement !== undefined) {
return {element: anchorElement, ref: anchorElementRef};
} else if (anchorRef) {
return {element: anchorRef.current, ref: anchorRef};
}
Expand Down
4 changes: 3 additions & 1 deletion src/components/Popup/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import type {OffsetOptions, Placement, VirtualElement} from '@floating-ui/react'

import type {AUTO_PLACEMENTS} from './constants';

export type PopupPlacement = (typeof AUTO_PLACEMENTS)[number] | Placement | Placement[];
export type AutoPlacement = (typeof AUTO_PLACEMENTS)[number];

export type PopupPlacement = AutoPlacement | Placement | Placement[];

export type PopupAnchorElement = Element | VirtualElement;

Expand Down
8 changes: 6 additions & 2 deletions src/components/Popup/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ARROW_SIZE} from './constants';
import type {PopupOffset} from './types';
import {ARROW_SIZE, AUTO_PLACEMENTS} from './constants';
import type {AutoPlacement, PopupOffset} from './types';

export function getOffsetValue(offset: PopupOffset, hasArrow: boolean | undefined) {
let offsetValue = offset;
Expand All @@ -13,3 +13,7 @@ export function getOffsetValue(offset: PopupOffset, hasArrow: boolean | undefine

return offsetValue;
}

export function isAutoPlacement(placement: string): placement is AutoPlacement {
return AUTO_PLACEMENTS.includes(placement as AutoPlacement);
}
2 changes: 1 addition & 1 deletion src/components/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const Tooltip = (props: TooltipProps) => {
style={style}
open={tooltipVisible && !disabled}
placement={placement}
anchorEl={anchorElement}
anchorElement={anchorElement}
disablePortal={disablePortal}
disableEscapeKeyDown
disableOutsideClick
Expand Down