diff --git a/.changeset/angry-mirrors-play.md b/.changeset/angry-mirrors-play.md new file mode 100644 index 0000000000..00024ecfb8 --- /dev/null +++ b/.changeset/angry-mirrors-play.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/hooks': minor +--- + +Add `useMergeRefs` hook for merging array of refs into a single memoized callback ref or `null` diff --git a/.changeset/loud-ears-share.md b/.changeset/loud-ears-share.md new file mode 100644 index 0000000000..10fdfc30b0 --- /dev/null +++ b/.changeset/loud-ears-share.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/popover': major +--- + +[LG-4447](https://jira.mongodb.org/browse/LG-4447) Remove unused `contentClassName` prop and abstract popover positioning logic into custom hooks diff --git a/.changeset/loud-items-thank.md b/.changeset/loud-items-thank.md new file mode 100644 index 0000000000..4eeca13751 --- /dev/null +++ b/.changeset/loud-items-thank.md @@ -0,0 +1,22 @@ +--- +'@lg-tools/codemods': minor +--- + +[LG-4525](https://jira.mongodb.org/browse/LG-4525) Add `popover-v12` codemod which can be used to refactor popover component instances. It does the following: +1. Adds an explicit `usePortal={true}` declaration if left undefined and consolidates the `usePortal` and `renderMode` props into a single `renderMode` prop for the following components: +- `Combobox` +- `Menu` +- `Popover` +- `Select` +- `SplitButton` +- `Tooltip` +2. Removes `popoverZIndex`, `portalClassName`, `portalContainer`, `portalRef`, `scrollContainer`, and `usePortal` props from the following components: +- `InfoSprinkle` +- `InlineDefinition` +- `NumberInput` +3. Removes `popoverZIndex`, `portalClassName`, `portalContainer`, `portalRef`, and `scrollContainer` props from the following components: +- `DatePicker` +- `GuideCue` +4. Removes `popoverZIndex`, `portalClassName`, `portalContainer`, `scrollContainer`, and `usePortal` props from `Code` component +5. Removes `portalClassName`, `portalContainer`, `portalRef`, `scrollContainer`, and `usePortal` props from `SearchInput` component +6. Removes `shouldTooltipUsePortal` prop from `Copyable` component diff --git a/.changeset/pink-worms-tap.md b/.changeset/pink-worms-tap.md new file mode 100644 index 0000000000..a8467e05c7 --- /dev/null +++ b/.changeset/pink-worms-tap.md @@ -0,0 +1,12 @@ +--- +'@leafygreen-ui/popover': major +'@leafygreen-ui/inline-definition': major +'@leafygreen-ui/info-sprinkle': major +'@leafygreen-ui/date-picker': major +'@leafygreen-ui/copyable': major +'@leafygreen-ui/tooltip': major +'@leafygreen-ui/code': major +'@leafygreen-ui/menu': major +--- + +[LG-4516](https://jira.mongodb.org/browse/LG-4516): Deprecates justify="fit"; use justify="middle" instead diff --git a/.changeset/popular-steaks-grab.md b/.changeset/popular-steaks-grab.md new file mode 100644 index 0000000000..06b3fe5ece --- /dev/null +++ b/.changeset/popular-steaks-grab.md @@ -0,0 +1,10 @@ +--- +'@leafygreen-ui/code': patch +'@leafygreen-ui/copyable': patch +'@leafygreen-ui/modal': patch +'@leafygreen-ui/popover': patch +--- + +[LG-4446](https://jira.mongodb.org/browse/LG-4446): In order to consolidate popover-related contexts, the `PortalContext` values for `portalContainer` and `scrollContainer` are consolidated in the `PopoverContext`. + +`usePopoverPortalContainer` is replaced by `usePopoverContext` and `PortalContextProvider` is replaced by `PopoverProvider`. There are no functional changes. diff --git a/.changeset/smooth-experts-admire.md b/.changeset/smooth-experts-admire.md new file mode 100644 index 0000000000..5874537516 --- /dev/null +++ b/.changeset/smooth-experts-admire.md @@ -0,0 +1,29 @@ +--- +'@leafygreen-ui/leafygreen-provider': major +--- + +[LG-4446](https://jira.mongodb.org/browse/LG-4446): In order to consolidate popover-related contexts, the `PortalContext` values for `portalContainer` and `scrollContainer` are consolidated in the `PopoverContext`. + +`usePopoverPortalContainer` is replaced by `usePopoverContext` and `PortalContextProvider` is replaced by `PopoverProvider`. + +`usePopoverPortalContainer` (used internally) and `PortalContextProvider` are no longer exported. See below migration guidance. + +#### Migration guide + +##### Old +```js + +``` + +##### New +```js + +``` diff --git a/.changeset/tall-chefs-fail.md b/.changeset/tall-chefs-fail.md new file mode 100644 index 0000000000..3b1c03bd46 --- /dev/null +++ b/.changeset/tall-chefs-fail.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/popover': minor +--- + +[LG-4515](https://jira.mongodb.org/browse/LG-4515) Replace internal position utils with [@floating-ui/react](https://floating-ui.com/docs/useFloating) diff --git a/.changeset/tame-islands-count.md b/.changeset/tame-islands-count.md new file mode 100644 index 0000000000..517b99531b --- /dev/null +++ b/.changeset/tame-islands-count.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/date-picker': patch +--- + +Remove unused popover `contentClassName` prop diff --git a/.changeset/two-pants-poke.md b/.changeset/two-pants-poke.md new file mode 100644 index 0000000000..0bebf79179 --- /dev/null +++ b/.changeset/two-pants-poke.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/popover': minor +--- + +Update default value of `spacing` prop from 10px to 4px diff --git a/chat/fixed-chat-window/src/FixedChatWindow.stories.tsx b/chat/fixed-chat-window/src/FixedChatWindow.stories.tsx index e66891405f..541a538e57 100644 --- a/chat/fixed-chat-window/src/FixedChatWindow.stories.tsx +++ b/chat/fixed-chat-window/src/FixedChatWindow.stories.tsx @@ -22,6 +22,19 @@ const meta: StoryMetaType = { parameters: { default: 'Uncontrolled', }, + decorators: [ + StoryFn => ( +
+ +
+ ), + ], }; export default meta; diff --git a/chat/input-bar/src/InputBar/InputBar.types.ts b/chat/input-bar/src/InputBar/InputBar.types.ts index 9cd8ad3874..aaaa097d42 100644 --- a/chat/input-bar/src/InputBar/InputBar.types.ts +++ b/chat/input-bar/src/InputBar/InputBar.types.ts @@ -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 & { @@ -48,7 +48,7 @@ export type InputBarProps = HTMLElementProps<'form'> & /** * Props passed to the Popover that renders the suggested promps. */ - dropdownProps?: PortalControlProps; + dropdownProps?: Omit; }; export type { TextareaAutosizeProps }; diff --git a/packages/chip/README.md b/packages/chip/README.md index e626376e61..eb7cc40d16 100644 --- a/packages/chip/README.md +++ b/packages/chip/README.md @@ -33,7 +33,6 @@ or baseFontSize={13} disabled onDismiss={() => {}} - popoverZIndex={1} chipCharacterLimit={10} chipTruncationLocation="end" dismissButtonAriaLabel="aria-label" @@ -49,7 +48,6 @@ or | `label` | `React.ReactNode` | Label rendered in the chip | | | `chipTruncationLocation` | `'end'` \| `'middle'` \| `'none'` \| `'start'` | Defines where the ellipses will appear in a Chip when the label length exceeds the `chipCharacterLimit`. If `none` is passed, the chip will not truncate. **Note**: If there is any truncation, the full label text will appear inside a tooltip on hover | `none` | | `chipCharacterLimit` | `number` | Defines the character limit of a Chip before they start truncating. **Note**: the three ellipses dots are included in the character limit and the chip will only truncate if the chip length is greater than the `chipCharacterLimit`. | | -| `popoverZIndex` | `number` | Number that controls the z-index of the tooltip containing the full label text. | | | `baseFontSize` | `'13'` \| `'16'` | Determines the base font-size of the chip. | | | `variant` | `'gray'` \| `'blue'` \| `'green'` \| `'purple'` \| `'red'` \| `'yellow'` \| `'blue'` | The color of the chip. | | | `glyph` | `React.ReactElement` | An icon glyph rendered before the text. To use a custom icon, see [Link](https://github.com/mongodb/leafygreen-ui/blob/main/packages/icon/README.md#usage-registering-custom-icon-sets) docs | | diff --git a/packages/chip/src/Chip/Chip.spec.tsx b/packages/chip/src/Chip/Chip.spec.tsx index c06de541d9..706db75e12 100644 --- a/packages/chip/src/Chip/Chip.spec.tsx +++ b/packages/chip/src/Chip/Chip.spec.tsx @@ -353,7 +353,6 @@ describe('packages/chip', () => { baseFontSize={13} disabled onDismiss={() => {}} - popoverZIndex={1} chipCharacterLimit={10} chipTruncationLocation="end" dismissButtonAriaLabel="deselect" diff --git a/packages/chip/src/Chip/Chip.styles.ts b/packages/chip/src/Chip/Chip.styles.ts index 99f6f81792..caa4a47248 100644 --- a/packages/chip/src/Chip/Chip.styles.ts +++ b/packages/chip/src/Chip/Chip.styles.ts @@ -329,3 +329,7 @@ export const getTextStyles = ( [textDisabledStyles(theme)]: isDisabled, [textDismissibleStyles]: isDismissible, }); + +export const inlineDefinitionStyles = css` + white-space: normal; +`; diff --git a/packages/chip/src/Chip/Chip.tsx b/packages/chip/src/Chip/Chip.tsx index 858a7513ae..999eb069b8 100644 --- a/packages/chip/src/Chip/Chip.tsx +++ b/packages/chip/src/Chip/Chip.tsx @@ -14,6 +14,7 @@ import { chipTextClassName, getTextStyles, getWrapperStyles, + inlineDefinitionStyles, } from './Chip.styles'; import { ChipProps, TruncationLocation, Variant } from './Chip.types'; @@ -28,7 +29,6 @@ export const Chip = React.forwardRef( darkMode: darkModeProp, label, onDismiss, - popoverZIndex, className, dismissButtonAriaLabel, glyph, @@ -82,8 +82,8 @@ export const Chip = React.forwardRef( darkMode={darkMode} definition={label} align="bottom" - popoverZIndex={popoverZIndex} className={chipInlineDefinitionClassName} + tooltipClassName={inlineDefinitionStyles} > {truncatedName} @@ -111,7 +111,6 @@ Chip.propTypes = { label: PropTypes.string.isRequired, chipCharacterLimit: PropTypes.number, chipTruncationLocation: PropTypes.oneOf(Object.values(TruncationLocation)), - popoverZIndex: PropTypes.number, baseFontSize: PropTypes.oneOf(Object.values(BaseFontSize)), variant: PropTypes.oneOf(Object.values(Variant)), onDismiss: PropTypes.func, diff --git a/packages/chip/src/Chip/Chip.types.ts b/packages/chip/src/Chip/Chip.types.ts index 4c60a8315d..a2c903b164 100644 --- a/packages/chip/src/Chip/Chip.types.ts +++ b/packages/chip/src/Chip/Chip.types.ts @@ -43,11 +43,6 @@ export interface ChipProps */ chipCharacterLimit?: number; - /** - * Number that controls the z-index of the tooltip containing the full label text. - */ - popoverZIndex?: number; - /** * Determines the base font-size of the component * diff --git a/packages/code/README.md b/packages/code/README.md index ce67888136..34e0383c74 100644 --- a/packages/code/README.md +++ b/packages/code/README.md @@ -119,11 +119,6 @@ const SomeComponent = () => {codeSnippet}; | `highlightLines` | `Array` | An optional array of lines to highlight. The array can only contain numbers corresponding to the line numbers to highlight, and / or tuples representing a range (e.g. `[6, 10]`); | | | `languageOptions` | `Array` (see below) | An array of language options. When provided, a LanguageSwitcher dropdown is rendered. | | | `onChange` | `(language: LanguageOption) => void` | A change handler triggered when the language is changed. Invalid when no `languageOptions` are provided | | -| `usePortal` | `boolean` | Will position the language switcher dropdown relative to its parent without using a Portal if `usePortal` is set to false. | `true` | -| `portalContainer` | `HTMLElement` \| `null` | Sets the container used for the language switcher's dropdown's portal. | | -| `scrollContainer` | `HTMLElement` \| `null` | If the language switcher's dropdown's portal has a scrollable ancestor other than the window, this prop allows passing a reference to that element to allow the portal to position properly. | | -| `portalClassName` | `string` | Passes the given className to the language switcher's dropdown's portal container if the default portal container is being used. | | -| `popoverZIndex` | `number` | Sets the z-index CSS property for the language switcher's dropdown. | | ``` interface LanguageOption { diff --git a/packages/code/src/Code/Code.tsx b/packages/code/src/Code/Code.tsx index 062054e4db..a57c262bbe 100644 --- a/packages/code/src/Code/Code.tsx +++ b/packages/code/src/Code/Code.tsx @@ -82,11 +82,6 @@ function Code({ onChange, customActionButtons = [], showCustomActionButtons = false, - usePortal = true, - portalClassName, - portalContainer, - scrollContainer, - popoverZIndex, ...rest }: CodeProps) { const scrollableElementRef = useRef(null); @@ -205,18 +200,6 @@ function Code({ debounceScroll(e); }; - const popoverProps = { - popoverZIndex, - ...(usePortal - ? { - usePortal, - portalClassName, - portalContainer, - scrollContainer, - } - : { usePortal }), - } as const; - const showExpandButton = !!( expandable && numOfLinesOfCode && @@ -286,7 +269,6 @@ function Code({ isMultiline={isMultiline} customActionButtons={filteredCustomActionIconButtons} showCustomActionButtons={showCustomActionsInPanel} - {...popoverProps} /> )} diff --git a/packages/code/src/CopyButton/CopyButton.styles.ts b/packages/code/src/CopyButton/CopyButton.styles.ts index 381fd9d61c..67b845f29c 100644 --- a/packages/code/src/CopyButton/CopyButton.styles.ts +++ b/packages/code/src/CopyButton/CopyButton.styles.ts @@ -2,6 +2,13 @@ import { css } from '@leafygreen-ui/emotion'; import { Theme } from '@leafygreen-ui/lib'; import { palette } from '@leafygreen-ui/palette'; +export const tooltipStyles = css` + svg { + width: 26px; + height: 26px; + } +`; + export const copiedThemeStyle: Record = { [Theme.Light]: css` color: ${palette.white}; diff --git a/packages/code/src/CopyButton/CopyButton.tsx b/packages/code/src/CopyButton/CopyButton.tsx index 02a4362d73..dec55d0c12 100644 --- a/packages/code/src/CopyButton/CopyButton.tsx +++ b/packages/code/src/CopyButton/CopyButton.tsx @@ -9,37 +9,47 @@ import CopyIcon from '@leafygreen-ui/icon/dist/Copy'; import IconButton from '@leafygreen-ui/icon-button'; import { useDarkMode, - usePopoverPortalContainer, + usePopoverContext, } from '@leafygreen-ui/leafygreen-provider'; import { keyMap } from '@leafygreen-ui/lib'; -import Tooltip, { Align, Justify } from '@leafygreen-ui/tooltip'; +import Tooltip, { + Align, + hoverDelay, + Justify, + RenderMode, +} from '@leafygreen-ui/tooltip'; import { COPIED_SUCCESS_DURATION, COPIED_TEXT, COPY_TEXT } from './constants'; -import { copiedThemeStyle, copyButtonThemeStyles } from './CopyButton.styles'; +import { + copiedThemeStyle, + copyButtonThemeStyles, + tooltipStyles, +} from './CopyButton.styles'; import { CopyProps } from './CopyButton.types'; function CopyButton({ onCopy, contents }: CopyProps) { const [copied, setCopied] = useState(false); /** - * `CopyButton` controls `open` state of tooltip because when `copied` state + * `CopyButton` controls `tooltipOpen` state because when `copied` state * changes, it causes the tooltip to re-render */ - const [open, setOpen] = useState(false); + const [tooltipOpen, setTooltipOpen] = useState(false); const buttonRef = useRef(null); + const timeoutRef = useRef(null); const { theme } = useDarkMode(); - const { portalContainer } = usePopoverPortalContainer(); + const { portalContainer } = usePopoverContext(); /** - * toggles `open` state of tooltip + * toggles `tooltipOpen` state */ - const closeTooltip = () => setOpen(false); - const openTooltip = () => setOpen(true); + const closeTooltip = () => setTooltipOpen(false); + const openTooltip = () => setTooltipOpen(true); /** * forcibly closes tooltip if user tabs focus on tooltip and clicks * outside of the trigger */ - useBackdropClick(closeTooltip, buttonRef, open); + useBackdropClick(closeTooltip, buttonRef, tooltipOpen); useEffect(() => { if (!buttonRef.current) { @@ -90,14 +100,20 @@ function CopyButton({ onCopy, contents }: CopyProps) { }; /** - * `handleMouseEnter` and `handleMouseLeave` are used to control `open` + * `handleMouseEnter` and `handleMouseLeave` are used to control `tooltipOpen` * state when mouse hovers over tooltip trigger */ const handleMouseEnter = () => { - openTooltip(); + timeoutRef.current = setTimeout(() => { + openTooltip(); + }, hoverDelay); }; const handleMouseLeave = () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } closeTooltip(); }; @@ -105,15 +121,17 @@ function CopyButton({ onCopy, contents }: CopyProps) { * `shouldClose` indicates to `Tooltip` component that tooltip should * remain open even if trigger re-renders */ - const shouldClose = () => !open; + const shouldClose = () => !tooltipOpen; return ( ; onChange: (arg0: LanguageOption) => void; } -function LanguageSwitcher({ - language, - languageOptions, - onChange, - usePortal, - portalClassName, - portalContainer, - scrollContainer, - popoverZIndex, -}: Props) { +function LanguageSwitcher({ language, languageOptions, onChange }: Props) { const { theme, darkMode } = useDarkMode(); const previousLanguage = usePrevious(language); @@ -83,14 +74,6 @@ function LanguageSwitcher({ } } - const popoverProps = { - popoverZIndex, - usePortal, - portalClassName, - portalContainer, - scrollContainer, - } as const; - return (
` | `'none'` | -| `errorMessage` | `string` | Text that shows when the `state` is set to `error`. | `'This input needs your attention'` | -| `successMessage` | `string` | Text that shows when the `state` is set to `valid`. | `'Success'` | -| `baseFontSize` | `'13'`, `'16'` | Determines the base font size if sizeVariant is set to `default` | `'13'` | -| `dropdownWidthBasis` | `'option'` \| `'trigger'` | Determines the width of the dropdown. `trigger` will make the dropdown width the width of the menu trigger. `option` will make the dropdown width as wide as the widest option. | `trigger` | +| Prop | Type | Description | Default | +| -------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------------------------------------- | -------- | +| `children` | `node` | `