Skip to content

Commit

Permalink
refactor(floating): mv all floating components to same API
Browse files Browse the repository at this point in the history
h2. ~~`Tooltip`~~ -> [OnboardingTooltip](#/OnboardingTooltip)

```diff
<OnboardingTooltip
- isShown
+ isShown

- alignX="bottom"
- alignY="left"
+ placement="bottom-start"

- offsetX={0}
+ offsetByCrossAxis={0}

- offsetY={0}
+ offsetByMainAxis={0}

- cornerOffset={0}
- cornerAbsoluteOffset={0}
+ arrowCornerOffset={0}
+ arrowCornerAbsoluteOffset={0}
>
  <div>Target</div>
</OnboardingTooltip>
```

h2. ~~`TextTooltip`~~ -> [Tooltip](#/Tooltip)

```diff
<Tooltip
- autoUpdateOnTargetResize
- customMiddlewares
- renderContent
- getRef

- offsetSkidding={0}
+ offsetByCrossAxis={0}

- offsetDistance={0}
+ offsetByMainAxis={0}

- shownDelay={0}
- hideDelay={10}
+ hoverDelay={[0, 10]}

- forcePortal
+ usePortal

- portalRoot={someHTMLElement}
+ usePortal={someHTMLElement}
>
  <div>Target</div>
</Tooltip>
```

h1. ⚠️ Поправить в migration_v6.md

h2. [Popper](#/Popper)

```diff
<Popper
- renderContent

- arrowClassName=""
+ arrowProps={{ iconClassName: "" }}

- offsetDistance={0}
+ offsetByMainAxis={0}

- offsetSkidding={0}
+ offsetByCrossAxis={0}

- onPlacementChange={({ placement }) => {}}
+ onPlacementChange={(placement) => {}}

- forcePortal
+ usePortal

- portalRoot={someHTMLElement}
+ usePortal={someHTMLElement}
/>
  <div>Target</div>
</Popper>
```

- `onPlacementChange` теперь вызывается только в случае, если `Popper` подобрал оптимальный
  `placement` вместо пользовательского.
- `renderContent` удалён в пользу `children`. Раньше из `renderContent` можно было получить
  `className`, который задаёт `Popper`, сейчас этот `className` пустой.
- `targetRef` теперь умеет принимать `VirtualElement`.
- `arrowProps` принимает атрибуты `HTMLDivElement`, а также `iconStyle` и `iconClassName`.
  • Loading branch information
inomdzhon committed Dec 5, 2023
1 parent 6e5a521 commit bfe42ed
Show file tree
Hide file tree
Showing 66 changed files with 1,512 additions and 1,527 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export const ActionSheetDropdownMenu = ({
)}
style={style}
getRootRef={elementRef}
forcePortal={false}
usePortal={false}
>
<FocusTrap onClose={onClose} {...restProps} onClick={onClick}>
{children}
Expand Down
2 changes: 1 addition & 1 deletion packages/vkui/src/components/ActionSheet/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import type { PlacementWithAuto } from '../../lib/floating/types';
import type { PlacementWithAuto } from '../../lib/floating/types/common';
import { FocusTrapProps } from '../FocusTrap/FocusTrap';

export type ToggleRef = Element | null | undefined | React.RefObject<Element>;
Expand Down
77 changes: 34 additions & 43 deletions packages/vkui/src/components/AppRoot/AppRootPortal.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
import * as React from 'react';
import { createPortal } from 'react-dom';
import { useAppearance } from '../../hooks/useAppearance';
import { useIsClient } from '../../hooks/useIsClient';
import { createPortal } from '../../lib/createPortal';
import { isRefObject } from '../../lib/isRefObject';
import { HasChildren } from '../../types';
import { AppearanceProvider } from '../AppearanceProvider/AppearanceProvider';
import { AppRootContext } from './AppRootContext';
import { AppRootContext, type AppRootContextInterface } from './AppRootContext';

export interface AppRootPortalProps extends HasChildren {
className?: string;
forcePortal?: boolean;
/**
* Кастомный root-элемент портала.
* При передаче вместе с `forcePorta=true` игнорируется `portalRoot` и `disablePortal`
* из контекста `AppRoot`.
* - При передаче `true` будет использовать `portalRoot` из контекста `AppRoot`.
* - При передаче элемента будут игнорироваться `portalRoot` и `disablePortal` из контекста `AppRoot`.
*/
portalRoot?: HTMLElement | React.RefObject<HTMLElement> | null;
usePortal?: boolean | HTMLElement | React.RefObject<HTMLElement> | null;
}

export const AppRootPortal = ({
children,
className,
forcePortal: forcePortalProp,
portalRoot: portalRootProp = null,
}: AppRootPortalProps) => {
export const AppRootPortal = ({ children, usePortal }: AppRootPortalProps) => {
const { portalRoot, mode, disablePortal } = React.useContext(AppRootContext);
const appearance = useAppearance();

Expand All @@ -32,40 +24,39 @@ export const AppRootPortal = ({
return null;
}

const forcePortal = forcePortalProp ?? mode !== 'full';

const portalContainer = getPortalContainer(portalRootProp, portalRoot);

const ignoreDisablePortalFlagFromContext = portalRootProp && forcePortal;
const shouldUsePortal = ignoreDisablePortalFlagFromContext
? true
: !disablePortal && portalContainer && forcePortal;
const portalContainer = resolvePortalContainer(usePortal, portalRoot);
if (!portalContainer || shouldDisablePortal(usePortal, mode, Boolean(disablePortal))) {
return children;
}

return shouldUsePortal && portalContainer ? (
createPortal(
<AppearanceProvider value={appearance}>
<div className={className}>{children}</div>
</AppearanceProvider>,
portalContainer,
)
) : (
<React.Fragment>{children}</React.Fragment>
return createPortal(
<AppearanceProvider value={appearance}>{children}</AppearanceProvider>,
portalContainer,
);
};

/**
* Получает из кастомного пропа `partialRootProp` и `partialRoot` контекста
* контейнер-элемент для портала.
* `partialRootProp` может быть ref элементом.
*
*/
function getPortalContainer(
portalRootProp?: HTMLElement | React.RefObject<HTMLElement> | null,
portalRoot?: HTMLElement | null,
function shouldDisablePortal(
usePortal: AppRootPortalProps['usePortal'],
mode: AppRootContextInterface['mode'],
disablePortal: boolean,
) {
if (usePortal !== undefined) {
if (typeof usePortal !== 'boolean') {
return false;

Check warning on line 45 in packages/vkui/src/components/AppRoot/AppRootPortal.tsx

View check run for this annotation

Codecov / codecov/patch

packages/vkui/src/components/AppRoot/AppRootPortal.tsx#L44-L45

Added lines #L44 - L45 were not covered by tests
}
return disablePortal || usePortal !== true;

Check warning on line 47 in packages/vkui/src/components/AppRoot/AppRootPortal.tsx

View check run for this annotation

Codecov / codecov/patch

packages/vkui/src/components/AppRoot/AppRootPortal.tsx#L47

Added line #L47 was not covered by tests
}
// fallback
return disablePortal || mode !== 'full';
}

function resolvePortalContainer<PortalRootFromContext extends HTMLElement | null | undefined>(
usePortal: AppRootPortalProps['usePortal'],
portalRootFromContext: PortalRootFromContext,
) {
if (!portalRootProp) {
return portalRoot;
if (usePortal === true || !usePortal) {
return portalRootFromContext ? portalRootFromContext : null;
}

return isRefObject(portalRootProp) ? portalRootProp.current : portalRootProp;
return isRefObject(usePortal) ? usePortal.current : usePortal;

Check warning on line 61 in packages/vkui/src/components/AppRoot/AppRootPortal.tsx

View check run for this annotation

Codecov / codecov/patch

packages/vkui/src/components/AppRoot/AppRootPortal.tsx#L61

Added line #L61 was not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const CustomSelectDropdown = ({
),
className,
)}
forcePortal={forcePortal}
usePortal={forcePortal}
autoUpdateOnTargetResize
{...restProps}
>
Expand Down
6 changes: 3 additions & 3 deletions packages/vkui/src/components/FixedLayout/FixedLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { useGlobalEventListener } from '../../hooks/useGlobalEventListener';
import { usePlatform } from '../../hooks/usePlatform';
import { useDOM } from '../../lib/dom';
import { HTMLAttributesWithRootRef } from '../../types';
import { OnboardingTooltipContainer } from '../OnboardingTooltip/OnboardingTooltipContainer';
import { SplitColContext } from '../SplitCol/SplitColContext';
import { TooltipContainer } from '../Tooltip/TooltipContainer';
import styles from './FixedLayout.module.css';

const stylesVertical = {
Expand Down Expand Up @@ -79,7 +79,7 @@ export const FixedLayout = ({
useGlobalEventListener(window, 'resize', doResize);

return (
<TooltipContainer
<OnboardingTooltipContainer
{...restProps}
fixed
ref={ref}
Expand All @@ -93,6 +93,6 @@ export const FixedLayout = ({
style={{ ...style, width }}
>
{children}
</TooltipContainer>
</OnboardingTooltipContainer>
);
};
102 changes: 0 additions & 102 deletions packages/vkui/src/components/HoverPopper/HoverPopper.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import {
type ComponentPlaygroundProps,
TEST_CLASS_NAMES,
} from '@vkui-e2e/playground-helpers';
import { Tooltip, type TooltipProps } from './Tooltip';
import { TooltipContainer } from './TooltipContainer';
import { OnboardingTooltip, type OnboardingTooltipProps } from './OnboardingTooltip';
import { OnboardingTooltipContainer } from './OnboardingTooltipContainer';

export const TooltipPlayground = (props: ComponentPlaygroundProps) => {
export const OnboardingTooltipPlayground = (props: ComponentPlaygroundProps) => {
return (
<ComponentPlayground
{...props}
Expand All @@ -16,39 +16,36 @@ export const TooltipPlayground = (props: ComponentPlaygroundProps) => {
header: [undefined, 'header'],
},
{
alignX: ['left', 'right'],
alignY: ['top', 'bottom'],
placement: ['top-start', 'top-end', 'bottom-start', 'bottom-end'],
},
{
alignX: ['left'],
alignY: ['top'],
cornerOffset: [5, -5],
placement: ['top-start'],
arrowCornerOffset: [5, -5],
},
{
alignX: ['left'],
alignY: ['top'],
cornerAbsoluteOffset: [10, -1],
placement: ['top-start'],
arrowCornerAbsoluteOffset: [10, -1],
},
]}
>
{(props: TooltipProps) => (
<TooltipContainer
{(props: OnboardingTooltipProps) => (
<OnboardingTooltipContainer
style={{
minWidth: '300px',
height: '100px',
position: 'relative',
display: 'flex',
border: '1px solid #eee',
alignItems: props.alignY === 'top' ? 'flex-end' : 'flex-start',
alignItems: props.placement?.startsWith('top') ? 'flex-end' : 'flex-start',
justifyContent: 'center',
}}
>
<Tooltip text="text" {...props}>
<OnboardingTooltip text="text" {...props}>
<div className={TEST_CLASS_NAMES.CONTENT} style={{ display: 'flex' }}>
Tooltip target
</div>
</Tooltip>
</TooltipContainer>
</OnboardingTooltip>
</OnboardingTooltipContainer>
)}
</ComponentPlayground>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as React from 'react';
import { test } from '@vkui-e2e/test';
import { OnboardingTooltipPlayground } from './OnboardingTooltip.e2e-playground';

test('OnboardingTooltip', async ({
mount,
expectScreenshotClippedToContent,
componentPlaygroundProps,
}) => {
await mount(<OnboardingTooltipPlayground {...componentPlaygroundProps} />);
await expectScreenshotClippedToContent();
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.Tooltip__overlay {
.OnboardingTooltip__overlay {
position: fixed;
inset-inline-start: 0;
inset-block-start: 0;
Expand Down
Loading

0 comments on commit bfe42ed

Please sign in to comment.