Skip to content

Commit

Permalink
LG-4445: add renderMode and top layer (#2482)
Browse files Browse the repository at this point in the history
* Fix usePortal={false} transition and className order

* Replace usePortal with renderMode and add top layer render mode in Popover

* Replace usePortal with renderMode and add top layer render mode in LG Provider

* Refactor types in Popover-consuming packages

* README

* Address feedback
  • Loading branch information
stephl3 authored Oct 4, 2024
1 parent 5161fe0 commit 6c8a4cd
Show file tree
Hide file tree
Showing 30 changed files with 1,879 additions and 837 deletions.
4 changes: 2 additions & 2 deletions chat/input-bar/src/InputBar/InputBar.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FormEvent, ReactElement } from 'react';
import { TextareaAutosizeProps } from 'react-textarea-autosize';

import { DarkModeProps, HTMLElementProps } from '@leafygreen-ui/lib';
import { PortalControlProps } from '@leafygreen-ui/popover';
import { PopoverRenderModeProps } from '@leafygreen-ui/popover';

export type InputBarProps = HTMLElementProps<'form'> &
DarkModeProps & {
Expand Down Expand Up @@ -48,7 +48,7 @@ export type InputBarProps = HTMLElementProps<'form'> &
/**
* Props passed to the Popover that renders the suggested promps.
*/
dropdownProps?: PortalControlProps;
dropdownProps?: Omit<PopoverRenderModeProps, 'renderMode'>;
};

export type { TextareaAutosizeProps };
4 changes: 2 additions & 2 deletions packages/combobox/src/Combobox/Combobox.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { ReactNode } from 'react';

import { type ChipProps } from '@leafygreen-ui/chip';
import { Either, HTMLElementProps } from '@leafygreen-ui/lib';
import { PortalControlProps } from '@leafygreen-ui/popover';
import { PopoverRenderModeProps } from '@leafygreen-ui/popover';

import {
ComboboxSize,
Expand Down Expand Up @@ -60,7 +60,7 @@ type PartialChipProps = Pick<
>;

export type BaseComboboxProps = Omit<HTMLElementProps<'div'>, 'onChange'> &
PortalControlProps &
PopoverRenderModeProps &
PartialChipProps & {
/**
* Defines the Combobox Options by passing children. Must be `ComboboxOption` or `ComboboxGroup`
Expand Down
4 changes: 2 additions & 2 deletions packages/combobox/src/ComboboxMenu/ComboboxMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useAvailableSpace, useForwardedRef } from '@leafygreen-ui/hooks';
import Icon from '@leafygreen-ui/icon';
import { useDarkMode } from '@leafygreen-ui/leafygreen-provider';
import { palette } from '@leafygreen-ui/palette';
import Popover, { PortalControlProps } from '@leafygreen-ui/popover';
import Popover, { PopoverRenderModeProps } from '@leafygreen-ui/popover';
import { Error } from '@leafygreen-ui/typography';

import { ComboboxProps } from '../Combobox';
Expand All @@ -30,7 +30,7 @@ type ComboboxMenuProps = {
id: string;
labelId: string;
menuWidth: number;
} & PortalControlProps &
} & PopoverRenderModeProps &
Pick<
ComboboxProps<any>,
| 'searchLoadingMessage'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { HTMLElementProps } from '@leafygreen-ui/lib';
import { PopoverProps } from '@leafygreen-ui/popover';
import { PortalControlProps } from '@leafygreen-ui/popover';

export type DatePickerMenuProps = PortalControlProps &
Omit<PopoverProps, 'children'> &
export type DatePickerMenuProps = Omit<
PopoverProps,
'children' | 'renderMode' | 'usePortal'
> &
HTMLElementProps<'div'>;
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import Popover, { PopoverProps } from '@leafygreen-ui/popover';

import { menuStyles } from './MenuWrapper.styles';

export type MenuWrapperProps = PopoverProps & HTMLElementProps<'div'>;
export type MenuWrapperProps = Omit<
PopoverProps,
'dismissMode' | 'onToggle' | 'renderMode'
> &
HTMLElementProps<'div'>;

/**
* A simple styled popover component
Expand All @@ -24,6 +28,7 @@ export const MenuWrapper = forwardRef<HTMLDivElement, MenuWrapperProps>(
ref={fwdRef}
className={cx(menuStyles[theme], className)}
{...props}
usePortal
>
{/*
* Prevents the opening and closing state of a select dropdown from propagating up
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import { AutoComplete, DatePickerState } from './types';

export type ModifiedPopoverProps = Omit<
PopoverProps,
'usePortal' | 'refEl' | 'children' | 'className' | 'active' | 'onClick'
| 'usePortal'
| 'refEl'
| 'children'
| 'className'
| 'active'
| 'onClick'
| 'renderMode'
>;

export type BaseDatePickerProps = {
Expand Down
1 change: 1 addition & 0 deletions packages/guide-cue/src/GuideCue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ function GuideCue({
adjustOnMutation={true}
popoverZIndex={popoverZIndex}
{...sharedProps}
usePortal={true}
>
{/* The beacon is using the popover component to position itself */}
<div
Expand Down
1 change: 1 addition & 0 deletions packages/guide-cue/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type ModifiedTooltipProps = Omit<
| 'refEl'
| 'darkMode'
| 'initialOpen'
| 'renderMode'
>;

interface StandaloneProps {
Expand Down
7 changes: 5 additions & 2 deletions packages/leafygreen-provider/src/LeafyGreenContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function LeafyGreenProvider({
['portalContainer', 'scrollContainer'].includes(key),
),
);
const popoverContextContainerValues =
const { portalContainer, scrollContainer } =
popoverPortalContainerProp ?? inheritedPopoverContextContainers;

return (
Expand All @@ -74,7 +74,10 @@ function LeafyGreenProvider({
setDarkMode={setDarkMode}
>
<OverlayProvider>
<PopoverProvider {...popoverContextContainerValues}>
<PopoverProvider
portalContainer={portalContainer}
scrollContainer={scrollContainer}
>
{children}
</PopoverProvider>
</OverlayProvider>
Expand Down
175 changes: 115 additions & 60 deletions packages/leafygreen-provider/src/PopoverContext/PopoverContext.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,68 +15,123 @@ type TransitionLifecycleCallbacks = Pick<
'onEnter' | 'onEntering' | 'onEntered' | 'onExit' | 'onExiting' | 'onExited'
>;

type PortalControlProps =
| {
/**
* Specifies that the popover content should be rendered at the end of the DOM,
* rather than in the DOM tree.
*
* default: `true`
*/
usePortal?: true;

/**
* When usePortal is `true`, specifies a class name to apply to the root element of the portal.
*/
portalClassName?: string;

/**
* When usePortal is `true`, specifies an element to portal within. The default behavior is to generate a div at the end of the document to render within.
*/
portalContainer?: HTMLElement | null;

/**
* A ref for the portal element
*/
portalRef?: React.MutableRefObject<HTMLElement | null>;

/**
* When usePortal is `true`, specifies the scrollable element to position relative to.
*/
scrollContainer?: HTMLElement | null;
}
| {
/**
* Specifies that the popover content should be rendered at the end of the DOM,
* rather than in the DOM tree.
*
* default: `true`
*/
usePortal: false;

/**
* When usePortal is `true`, specifies a class name to apply to the root element of the portal.
*/
portalClassName?: undefined;

/**
* When usePortal is `true`, specifies an element to portal within. The default behavior is to generate a div at the end of the document to render within.
*/
portalContainer?: null;

/**
* A ref for the portal element
*/
portalRef?: undefined;

/**
* When usePortal is `true`, specifies the scrollable element to position relative to.
*/
scrollContainer?: null;
};
/**
* Options to control how the popover element is dismissed. This should not be altered
* because it is intended to have parity with the web-native {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/popover popover attribute}
* @param Auto will automatically handle dismissal on backdrop click or esc key press, ensuring only one popover is visible at a time
* @param Manual will require that the consumer handle dismissal manually
*/
const DismissMode = {
Auto: 'auto',
Manual: 'manual',
} as const;
type DismissMode = (typeof DismissMode)[keyof typeof DismissMode];

/** Local implementation of web-native `ToggleEvent` until we use typescript v5 */
interface ToggleEvent extends Event {
type: 'toggle';
newState: 'open' | 'closed';
oldState: 'open' | 'closed';
}

/** @deprecated - use {@link RenderTopLayerProps} */
interface RenderInlineProps {
/**
* Popover element will render inline with the reference element
*/
renderMode: 'inline';

/** Not used in this `renderMode` */
dismissMode?: never;

/** Not used in this `renderMode` */
onToggle?: never;

/** Not used in this `renderMode` */
portalClassName?: never;

/** Not used in this `renderMode` */
portalContainer?: never;

/** Not used in this `renderMode` */
portalRef?: never;

/** Not used in this `renderMode` */
scrollContainer?: never;
}

/** @deprecated - use {@link RenderTopLayerProps} */
interface RenderPortalProps {
/**
* Popover element will render in a provided `portalContainer` or in a new div appended to the body
*/
renderMode?: 'portal';

/** Not used in this `renderMode` */
dismissMode?: never;

/** Not used in this `renderMode` */
onToggle?: never;

/**
* Specifies a class name to apply to the portal element
*/
portalClassName?: string;

/**
* Specifies an element to portal within. If not provided, a div is generated at the end of the body
*/
portalContainer?: HTMLElement | null;

/**
* Passes a ref to forward to the portal element
*/
portalRef?: React.MutableRefObject<HTMLElement | null>;

/**
* Specifies the scrollable element to position relative to
*/
scrollContainer?: HTMLElement | null;
}

interface RenderTopLayerProps {
/**
* Popover element will render in the top layer
*/
renderMode?: 'top-layer';

/**
* Options to control how the popover element is dismissed
* - `'auto'` will automatically handle dismissal on backdrop click or key press, ensuring only one popover is visible at a time
* - `'manual'` will require that the consumer handle dismissal manually
*/
dismissMode?: DismissMode;

/**
* A callback function that is called when the popover is toggled
*/
onToggle?: (e: ToggleEvent) => void;

/** Not used in this `renderMode` */
portalClassName?: never;

/** Not used in this `renderMode` */
portalContainer?: never;

/** Not used in this `renderMode` */
portalRef?: never;

/** Not used in this `renderMode` */
scrollContainer?: never;
}

type PopoverRenderModeProps =
| RenderInlineProps
| RenderPortalProps
| RenderTopLayerProps;

export type PopoverProviderProps = TransitionLifecycleCallbacks &
PortalControlProps & {
PopoverRenderModeProps & {
/**
* Specifies the amount of spacing (in pixels) between the trigger element and the Popover content.
*
Expand Down
4 changes: 2 additions & 2 deletions packages/number-input/src/UnitSelect/UnitSelect.types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
PopoverProps as ImportedPopoverProps,
PortalControlProps,
PopoverRenderModeProps,
} from '@leafygreen-ui/popover';

import { Size, UnitOption } from '../NumberInput/NumberInput.types';

export type PopoverProps = PortalControlProps &
export type PopoverProps = PopoverRenderModeProps &
Pick<ImportedPopoverProps, 'popoverZIndex'>;

export type UnitSelectProps = {
Expand Down
Loading

0 comments on commit 6c8a4cd

Please sign in to comment.