Skip to content

Commit

Permalink
feat(tooltip): Allow defining custom root element for tooltips (#3696)
Browse files Browse the repository at this point in the history
This is done to allow overriding where the tooltip is rendered
  • Loading branch information
yusijs authored Nov 29, 2024
1 parent 5a45b74 commit 69e0a8c
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type Density = 'compact' | 'comfortable'

type State = {
density: Density
rootElement?: HTMLElement | null
}

const initalState: State = {
Expand All @@ -26,12 +27,14 @@ const EdsContext = createContext<State>(initalState)

export type EdsProviderProps = {
density?: Density
rootElement?: HTMLElement | null
children: ReactNode
}

export const EdsProvider: React.FC<EdsProviderProps> = ({
children,
density,
rootElement,
}) => {
const [state, setState] = useState<State>({
...initalState,
Expand All @@ -49,6 +52,7 @@ export const EdsProvider: React.FC<EdsProviderProps> = ({

const value = {
density: state.density,
rootElement,
setDensity,
}
return <EdsContext.Provider value={value}>{children}</EdsContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,8 @@ Instead use `aria-disabled` and disable the `onClick` in the code. This has the
keyboard navigation, as the button is still focusable.

<Canvas of={ComponentStories.TooltipOnButton} />

### Custom root element
Some apps might not want to use the default root element for the tooltip. This can be changed by passing a custom `portalContainer` to the `Tooltip` component, or by setting the `rootElement` prop on the `EdsProvider`

<Canvas of={ComponentStories.CustomRootElement} />
32 changes: 32 additions & 0 deletions packages/eds-core-react/src/components/Tooltip/Tooltip.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import {
Table,
Icon,
Checkbox,
EdsProvider,
} from '../..'
import { data, columns, toCellValues } from '../../stories'
import { StoryFn, Meta } from '@storybook/react'
import { explore } from '@equinor/eds-icons'
import { Stack } from './../../../.storybook/components'
import page from './Tooltip.docs.mdx'
import { useState } from 'react'

const meta: Meta<typeof Tooltip> = {
title: 'Data Display/Tooltip',
Expand Down Expand Up @@ -169,3 +171,33 @@ export const TooltipOnButton: StoryFn<TooltipProps> = () => (
</>
)
TooltipOnButton.storyName = 'Tooltip on disabled Button'

export const CustomRootElement: StoryFn<TooltipProps> = () => {
const [element, setElement] = useState<null | HTMLElement>(null)

return (
<>
<div ref={(el) => setElement(el)}>#root</div>
<EdsProvider rootElement={element}>
<Tooltip
title={
'This tooltip renders within the #root div, set from EdsProvider'
}
>
<Icon data={explore} />
</Tooltip>
</EdsProvider>

<Tooltip
title={
'This tooltip renders within the #root div, but from portalContainer prop'
}
portalContainer={element}
>
<Icon data={explore} />
</Tooltip>
</>
)
}

CustomRootElement.storyName = `Custom portal element`
23 changes: 21 additions & 2 deletions packages/eds-core-react/src/components/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
useRole,
useDismiss,
} from '@floating-ui/react'
import { useEds } from '../EdsProvider'

const StyledTooltip = styled('div').withConfig({
shouldForwardProp: () => true, //workaround to avoid warning until popover gets added to react types
Expand Down Expand Up @@ -86,16 +87,29 @@ export type TooltipProps = {
children: React.ReactElement & React.RefAttributes<HTMLElement>
/** Delay in ms, default 100 */
enterDelay?: number
/** Portal container
* @default document.body
* */
portalContainer?: HTMLElement
} & HTMLAttributes<HTMLDivElement>

export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
function Tooltip(
{ title, placement = 'bottom', children, style, enterDelay = 100, ...rest },
{
title,
placement = 'bottom',
children,
style,
enterDelay = 100,
portalContainer,
...rest
},
ref,
) {
const arrowRef = useRef<HTMLDivElement>(null)
const [open, setOpen] = useState(false)
const shouldOpen = title !== '' && typeof document !== 'undefined'
const { rootElement } = useEds()

const {
x,
Expand Down Expand Up @@ -207,7 +221,12 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(

return (
<>
{shouldOpen && open && createPortal(TooltipEl, document.body)}
{shouldOpen &&
open &&
createPortal(
TooltipEl,
portalContainer ?? rootElement ?? document.body,
)}
{updatedChildren}
</>
)
Expand Down

0 comments on commit 69e0a8c

Please sign in to comment.