diff --git a/.changeset/curvy-dragons-rest.md b/.changeset/curvy-dragons-rest.md new file mode 100644 index 0000000000..4ef6a4b9c2 --- /dev/null +++ b/.changeset/curvy-dragons-rest.md @@ -0,0 +1,12 @@ +--- +"@channel.io/bezier-react": major +--- + +**Breaking Changes: Remove `TooltipProvider` and Property updates in `Tooltip` component** + +- No longer support `TooltipProvider` and `TooltipProviderProps`. `Tooltip` component was implemented via radix-ui's Tooltip, which required the use of a `TooltipProvider`, which caused all subcomponents to be re-rendered and caused a performance hit. We decided that the ability to share hover delay time between `Tooltip` components via `TooltipProvider` was not a feature we needed, even with the performance penalty. Also, by providing `TooltipProvider` built-in to `AppProvider`, we were unnecessarily importing modules from our library usage that didn't require `Tooltip`. +- `Tooltip` component now contains a `TooltipProvider` inside it. + +**Minor Changes:** + +- Change the default value of `delayShow` prop from `300` to `0`. diff --git a/packages/bezier-react/src/components/AppProvider/AppProvider.tsx b/packages/bezier-react/src/components/AppProvider/AppProvider.tsx index f587fa8580..6000cdbc2f 100644 --- a/packages/bezier-react/src/components/AppProvider/AppProvider.tsx +++ b/packages/bezier-react/src/components/AppProvider/AppProvider.tsx @@ -4,7 +4,6 @@ import { window as defaultWindow } from '~/src/utils/dom' import { FeatureProvider } from '~/src/components/FeatureProvider' import { TokenProvider } from '~/src/components/TokenProvider' -import { TooltipProvider } from '~/src/components/Tooltip' import { WindowProvider } from '~/src/components/WindowProvider' import { type AppProviderProps } from './AppProvider.types' @@ -50,9 +49,7 @@ export function AppProvider({ - - { children } - + { children } diff --git a/packages/bezier-react/src/components/Tooltip/Tooltip.test.tsx b/packages/bezier-react/src/components/Tooltip/Tooltip.test.tsx index 007fe0ddcb..cfc8ae5074 100644 --- a/packages/bezier-react/src/components/Tooltip/Tooltip.test.tsx +++ b/packages/bezier-react/src/components/Tooltip/Tooltip.test.tsx @@ -8,14 +8,8 @@ import userEvent from '@testing-library/user-event' import { render } from '~/src/utils/test' -import { - Tooltip, - TooltipProvider, -} from './Tooltip' -import { - type TooltipProps, - type TooltipProviderProps, -} from './Tooltip.types' +import { Tooltip } from './Tooltip' +import { type TooltipProps } from './Tooltip.types' describe('Tooltip', () => { const renderTooltip = ({ @@ -74,6 +68,12 @@ describe('Tooltip', () => { expect(queryByRole('tooltip')).not.toBeInTheDocument() }) + it('When the tooltip content is visible, clicking outside the tooltip should hide the tooltip content.', async () => { + const { queryByRole } = renderTooltip({ defaultShow: true, content: 'tooltip content' }) + await user.click(document.body) + expect(queryByRole('tooltip')).not.toBeInTheDocument() + }) + it('When the `allowHover` property is true, the tooltip content should be hoverable.', async () => { const { getByRole } = renderTooltip({ allowHover: true, content: 'tooltip content' }) await user.hover(getByRole('button')) @@ -150,55 +150,3 @@ describe('Tooltip', () => { }) }) }) - -describe('TooltipProvider', () => { - const renderTooltipProvider = ({ - children, - ...rest - }: TooltipProviderProps = {}) => render( - - - - - , - ) - - let user: ReturnType - - beforeEach(() => { - user = userEvent.setup() - }) - - describe('User Interactions', () => { - it('If the `allowHover` property is true, the tooltip content should be hoverable.', async () => { - const { getByRole } = renderTooltipProvider({ allowHover: true }) - await user.hover(getByRole('button')) - await user.hover(getByRole('tooltip')) - expect(getByRole('tooltip')).toBeInTheDocument() - }) - - it('If the `delayShow` property is greater than 0, the tooltip should be delayed by that number of ms before appearing.', async () => { - const { getByRole, queryByRole } = renderTooltipProvider({ delayShow: 1000 }) - await user.hover(getByRole('button')) - expect(queryByRole('tooltip')).not.toBeInTheDocument() - await waitFor(() => { - expect(queryByRole('tooltip')).toBeInTheDocument() - }, { timeout: 1000 }) - }) - - it('If the `delayHide` property is greater than 0, the tooltip should be delayed by that number of ms before disappearing.', async () => { - const { getByRole, queryByRole } = renderTooltipProvider({ delayHide: 1000 }) - await user.hover(getByRole('button')) - await user.unhover(getByRole('button')) - expect(queryByRole('tooltip')).toBeInTheDocument() - await waitFor(() => { - expect(queryByRole('tooltip')).not.toBeInTheDocument() - }, { timeout: 1000 }) - }) - }) -}) diff --git a/packages/bezier-react/src/components/Tooltip/Tooltip.tsx b/packages/bezier-react/src/components/Tooltip/Tooltip.tsx index 5647ce9268..903ec79651 100644 --- a/packages/bezier-react/src/components/Tooltip/Tooltip.tsx +++ b/packages/bezier-react/src/components/Tooltip/Tooltip.tsx @@ -9,11 +9,7 @@ import React, { import * as TooltipPrimitive from '@radix-ui/react-tooltip' -import { createContext } from '~/src/utils/react' -import { - isBoolean, - isEmpty, -} from '~/src/utils/type' +import { isEmpty } from '~/src/utils/type' import { Icon, @@ -27,7 +23,6 @@ import { useWindow } from '~/src/components/WindowProvider' import { TooltipPosition, type TooltipProps, - type TooltipProviderProps, } from './Tooltip.types' import styles from './Tooltip.module.scss' @@ -105,61 +100,16 @@ function getSideAndAlign( } } -const [ - /** - * NOTE: Custom context use because the radix-ui doesn't support `delayHide`. - */ - TooltipGlobalContextProvider, - useTooltipGlobalContext, -] = createContext> | null>(null, 'TooltipProvider') - -/** - * `TooltipProvider` is used to globally provide props to child tooltips. - * @example - * - * ```tsx - * - * - * - * ``` - */ -export function TooltipProvider({ - children, - allowHover = false, - delayShow = 300, - delayHide = 0, - skipDelayShow = 500, -}: TooltipProviderProps) { - const contextValue = useMemo(() => ({ - delayHide, - }), [delayHide]) - - return ( - - - { children } - - - ) -} - /** * `Tooltip` is a component that shows additional information when the mouse hovers or the keyboard is focused. + * If you want to compose another component with `Tooltip`, **it must spread props and forward ref.** * @example - * * ```tsx - * // Anatomy of the Tooltip - * - * - * - * - * // Example of a Tooltip with a button + * // Your component must spread props and forward ref. + * const Button = React.forwardRef((props, forwardedRef) => ( + * * * ``` */ @@ -175,22 +125,18 @@ export const Tooltip = forwardRef(function Tooltip icon, placement = TooltipPosition.BottomCenter, offset = 4, - container: givenContainer, + container: containerProp, keepInContainer = true, - allowHover, - delayShow, - delayHide: delayHideProp, + allowHover = false, + delayShow = 0, + delayHide = 0, ...rest }, forwardedRef) { - const { rootElement } = useWindow() const [show, setShow] = useState(defaultShow ?? false) const timeoutRef = useRef() - const { delayHide: globalDelayHide } = useTooltipGlobalContext('Tooltip') - const delayHide = delayHideProp ?? globalDelayHide - - const defaultContainer = rootElement - const container = givenContainer ?? defaultContainer + const { rootElement } = useWindow() + const container = containerProp ?? rootElement const shouldBeHidden = useMemo(() => ( disabled || isEmpty(content) @@ -257,76 +203,78 @@ export const Tooltip = forwardRef(function Tooltip ]) return ( - - - { children } - + + + + { children } + - - - - + + -
- { title && ( + +
+ { title && ( + + { title } + + ) } + - { title } + { content } - ) } - - { content } - + { description && ( + + { description } + + ) } +
- { description && ( - - { description } - + { icon && ( + ) } -
- - { icon && ( - - ) } -
-
-
-
-
+ + + + +
+ ) }) diff --git a/packages/bezier-react/src/components/Tooltip/Tooltip.types.ts b/packages/bezier-react/src/components/Tooltip/Tooltip.types.ts index b01dcf0d67..d01706d86e 100644 --- a/packages/bezier-react/src/components/Tooltip/Tooltip.types.ts +++ b/packages/bezier-react/src/components/Tooltip/Tooltip.types.ts @@ -25,30 +25,7 @@ export enum TooltipPosition { LeftBottom = 'leftBottom', } -interface TooltipProviderOptions { - /** - * Keeps the content of the tooltip open on hover. Disabling this feature affects accessibility. - * @default false - */ - allowHover?: boolean - /** - * The delay from when the mouse enters a tooltip trigger until the tooltip opens. - * @default 300 - */ - delayShow?: number - /** - * The delay from when the mouse leaves a tooltip content until the tooltip hides. - * @default 0 - */ - delayHide?: number - /** - * How much time a user has to enter another trigger without incurring a delay again. - * @default 500 - */ - skipDelayShow?: number -} - -interface TooltipOptions { +interface TooltipOwnProps { /** * The open state of the tooltip when it is initially rendered. */ @@ -89,19 +66,16 @@ interface TooltipOptions { keepInContainer?: boolean /** * Keeps the content of the tooltip open on hover. Disabling this feature affects accessibility. - * Inherits from the nearest `TooltipProvider`. * @default false */ allowHover?: boolean /** * The delay from when the mouse enters a tooltip trigger until the tooltip opens. - * Inherits from the nearest `TooltipProvider`. - * @default 300 + * @default 0 */ delayShow?: number /** * The delay from when the mouse leaves a tooltip content until the tooltip hides. - * Inherits from the nearest `TooltipProvider`. * @default 0 */ delayHide?: number @@ -125,13 +99,9 @@ interface TooltipOptions { onPointerDownOutside?: (event: CustomEvent<{ originalEvent: PointerEvent }>) => void } -export interface TooltipProviderProps extends - ChildrenProps, - TooltipProviderOptions {} - export interface TooltipProps extends Omit, 'title' | keyof ContentProps | keyof ChildrenProps>, ChildrenProps, ContentProps, DisableProps, - TooltipOptions {} + TooltipOwnProps {} diff --git a/packages/bezier-react/src/components/Tooltip/index.ts b/packages/bezier-react/src/components/Tooltip/index.ts index 0dffe57a72..a7c5c2bdfb 100644 --- a/packages/bezier-react/src/components/Tooltip/index.ts +++ b/packages/bezier-react/src/components/Tooltip/index.ts @@ -1,9 +1,5 @@ -export { - Tooltip, - TooltipProvider, -} from './Tooltip' +export { Tooltip } from './Tooltip' export { TooltipPosition, type TooltipProps, - type TooltipProviderProps, } from './Tooltip.types'