diff --git a/.changeset/selfish-foxes-punch.md b/.changeset/selfish-foxes-punch.md new file mode 100644 index 00000000000..eba3bc4d78e --- /dev/null +++ b/.changeset/selfish-foxes-punch.md @@ -0,0 +1,5 @@ +--- +'@itwin/itwinui-react': patch +--- + +Fine-tuned the default Tooltip delay to `{ open: 100ms, close: 200ms }`. diff --git a/.changeset/seven-parents-complain.md b/.changeset/seven-parents-complain.md new file mode 100644 index 00000000000..a2fd6ca4102 --- /dev/null +++ b/.changeset/seven-parents-complain.md @@ -0,0 +1,6 @@ +--- +'@itwin/itwinui-react': patch +--- + +Added `Tooltip` delay grouping to components that use `Tooltip` internally. This prevents the tooltip from lingering when quickly hovering across different parts of the component. +* Components affected: `SideNavigation`, `Slider`, `Stepper`, `List`. diff --git a/apps/react-workshop/src/AvatarGroup.test.ts b/apps/react-workshop/src/AvatarGroup.test.ts index fdd3f06e998..a3ca6df4b0d 100644 --- a/apps/react-workshop/src/AvatarGroup.test.ts +++ b/apps/react-workshop/src/AvatarGroup.test.ts @@ -19,7 +19,7 @@ describe('AvatarGroup', () => { if (testName.includes('Tooltip')) { cy.get('div').contains('3').trigger('mouseenter'); - cy.wait(50); + cy.wait(100); } cy.compareSnapshot(testName); diff --git a/apps/react-workshop/src/Breadcrumbs.test.ts b/apps/react-workshop/src/Breadcrumbs.test.ts index 4566dd99b00..da029a29130 100644 --- a/apps/react-workshop/src/Breadcrumbs.test.ts +++ b/apps/react-workshop/src/Breadcrumbs.test.ts @@ -27,7 +27,7 @@ describe('Breadcrumbs', () => { cy.get('button').eq(1).click(); } else if (testName === 'Custom Overflow Back Button') { cy.get('button').eq(1).trigger('mouseenter'); - cy.wait(60); + cy.wait(100); } cy.compareSnapshot(testName); diff --git a/apps/react-workshop/src/Stepper.test.ts b/apps/react-workshop/src/Stepper.test.ts index 0d3d7a93e8e..c2b00aaddd3 100644 --- a/apps/react-workshop/src/Stepper.test.ts +++ b/apps/react-workshop/src/Stepper.test.ts @@ -14,7 +14,7 @@ describe('Stepper', () => { if (testName.includes('Tooltip')) { cy.get('#ladle-root').within(() => { cy.get('li').first().trigger('mouseenter'); // trigger tooltip - cy.wait(50); + cy.wait(100); }); } diff --git a/apps/react-workshop/src/Tooltip.test.ts b/apps/react-workshop/src/Tooltip.test.ts index 677f336f6d2..c48b35ecdf5 100644 --- a/apps/react-workshop/src/Tooltip.test.ts +++ b/apps/react-workshop/src/Tooltip.test.ts @@ -11,7 +11,7 @@ describe('Tooltip', () => { const id = Cypress.storyId(storyPath, testName); cy.visit('/', { qs: { mode: 'preview', story: id } }); cy.get('#tooltip-target').trigger('mouseenter'); - cy.wait(50); + cy.wait(100); cy.compareSnapshot(testName); }); }); diff --git a/apps/react-workshop/src/WorkflowDiagram.test.ts b/apps/react-workshop/src/WorkflowDiagram.test.ts index 69f713b920b..0df7961779a 100644 --- a/apps/react-workshop/src/WorkflowDiagram.test.ts +++ b/apps/react-workshop/src/WorkflowDiagram.test.ts @@ -14,7 +14,7 @@ describe('WorkflowDiagram', () => { if (testName.includes('Tooltip')) { cy.get('#ladle-root').within(() => { cy.get('li').first().trigger('mouseenter'); // trigger tooltip - cy.wait(50); + cy.wait(100); }); } diff --git a/examples/DropdownMenu.hidemiddleware.css b/examples/DropdownMenu.hidemiddleware.css index e1878b09a25..41f56961887 100644 --- a/examples/DropdownMenu.hidemiddleware.css +++ b/examples/DropdownMenu.hidemiddleware.css @@ -1,7 +1,3 @@ -.demo-container { - width: 50%; -} - .list { overflow-y: auto; max-height: 200px; diff --git a/examples/DropdownMenu.hidemiddleware.jsx b/examples/DropdownMenu.hidemiddleware.jsx index 13d387c7071..d152c6e8cdf 100644 --- a/examples/DropdownMenu.hidemiddleware.jsx +++ b/examples/DropdownMenu.hidemiddleware.jsx @@ -29,7 +29,7 @@ export default () => { const items = new Array(30).fill(0); return ( - + {items.map((_, i) => ( diff --git a/packages/itwinui-react/src/core/ButtonGroup/ButtonGroup.tsx b/packages/itwinui-react/src/core/ButtonGroup/ButtonGroup.tsx index 188653dcde2..0bc0571c163 100644 --- a/packages/itwinui-react/src/core/ButtonGroup/ButtonGroup.tsx +++ b/packages/itwinui-react/src/core/ButtonGroup/ButtonGroup.tsx @@ -14,6 +14,7 @@ import { CompositeItem, FloatingDelayGroup, } from '@floating-ui/react'; +import { defaultTooltipDelay } from '../Tooltip/Tooltip.js'; // ---------------------------------------------------------------------------- @@ -131,7 +132,7 @@ export const ButtonGroup = React.forwardRef((props, forwardedRef) => { ); return ( - + {props.role === 'toolbar' ? ( { + const { className, ...rest } = props; + + return ( + + + + ); +}) as PolymorphicForwardRefComponent<'div'>; if (process.env.NODE_ENV === 'development') { List.displayName = 'List'; } diff --git a/packages/itwinui-react/src/core/SideNavigation/SideNavigation.tsx b/packages/itwinui-react/src/core/SideNavigation/SideNavigation.tsx index 8b98509b048..bbc9fad704f 100644 --- a/packages/itwinui-react/src/core/SideNavigation/SideNavigation.tsx +++ b/packages/itwinui-react/src/core/SideNavigation/SideNavigation.tsx @@ -12,6 +12,8 @@ import { } from '../../utils/index.js'; import type { PolymorphicForwardRefComponent } from '../../utils/index.js'; import { IconButton } from '../Buttons/IconButton.js'; +import { FloatingDelayGroup } from '@floating-ui/react'; +import { defaultTooltipDelay } from '../Tooltip/Tooltip.js'; // ---------------------------------------------------------------------------- @@ -142,41 +144,43 @@ export const SideNavigation = React.forwardRef((props, forwardedRef) => { className={cx('iui-side-navigation-wrapper', wrapperProps?.className)} ref={forwardedRef} > - - {expanderPlacement === 'top' && ExpandButton} + + {expanderPlacement === 'top' && ExpandButton} - {items} - - - {secondaryItems} + + {items} + + + {secondaryItems} + + {expanderPlacement === 'bottom' && ExpandButton} - {expanderPlacement === 'bottom' && ExpandButton} - + {submenu && ( { {minValueLabel} )} - - {currentValues.map((thumbValue, index) => { - const [minVal, maxVal] = getAllowableThumbRange(index); - const thisThumbProps = thumbProps?.(index); - return ( - - ); - })} - - + + + {currentValues.map((thumbValue, index) => { + const [minVal, maxVal] = getAllowableThumbRange(index); + const thisThumbProps = thumbProps?.(index); + return ( + + ); + })} + + + {tickMarkArea} {maxValueLabel && ( void vi.advanceTimersByTime(50)); + act(() => void vi.advanceTimersByTime(100)); const tooltip = document.querySelector('.iui-tooltip') as HTMLElement; expect(tooltip).toBeVisible(); expect(tooltip).toHaveTextContent('Step one tooltip'); @@ -282,7 +282,7 @@ it('should display tooltip upon hovering step if description provided', async () act(() => void vi.advanceTimersByTime(250)); fireEvent.mouseEnter(screen.getByText('Step Three'), { bubbles: true }); - act(() => void vi.advanceTimersByTime(50)); + act(() => void vi.advanceTimersByTime(200)); expect(tooltip).not.toBeVisible(); vi.useRealTimers(); diff --git a/packages/itwinui-react/src/core/Stepper/Stepper.tsx b/packages/itwinui-react/src/core/Stepper/Stepper.tsx index 2856f54f481..95cdb8af8bc 100644 --- a/packages/itwinui-react/src/core/Stepper/Stepper.tsx +++ b/packages/itwinui-react/src/core/Stepper/Stepper.tsx @@ -7,6 +7,8 @@ import * as React from 'react'; import { Box } from '../../utils/index.js'; import type { PolymorphicForwardRefComponent } from '../../utils/index.js'; import { StepperStep } from './StepperStep.js'; +import { FloatingDelayGroup } from '@floating-ui/react'; +import { defaultTooltipDelay } from '../Tooltip/Tooltip.js'; export type StepperLocalization = { stepsCountLabel: (currentStep: number, totalSteps: number) => string; @@ -103,31 +105,33 @@ export const Stepper = React.forwardRef((props, ref) => { return ( -
    - {steps.map((s, index) => { - const thisStepProps = stepProps?.(index); - const thisTrackContentProps = trackContentProps?.(index); - const thisCircleProps = circleProps?.(index); - const thisNameProps = nameProps?.(index); - return ( - - ); - })} -
+ +
    + {steps.map((s, index) => { + const thisStepProps = stepProps?.(index); + const thisTrackContentProps = trackContentProps?.(index); + const thisCircleProps = circleProps?.(index); + const thisNameProps = nameProps?.(index); + return ( + + ); + })} +
+
{type === 'long' && ( void vi.advanceTimersByTime(50)); + act(() => void vi.advanceTimersByTime(100)); const tooltip = document.querySelector('.iui-tooltip') as HTMLElement; expect(tooltip).toBeVisible(); expect(tooltip).toHaveTextContent('Step one tooltip'); @@ -90,7 +90,7 @@ it('should display tooltip upon hovering step if description provided', async () act(() => void vi.advanceTimersByTime(250)); fireEvent.mouseEnter(screen.getByText('Step Three'), { bubbles: true }); - act(() => void vi.advanceTimersByTime(50)); + act(() => void vi.advanceTimersByTime(100)); expect(document.querySelector('.iui-tooltip')).not.toBeVisible(); vi.useRealTimers(); diff --git a/packages/itwinui-react/src/core/Tooltip/Tooltip.tsx b/packages/itwinui-react/src/core/Tooltip/Tooltip.tsx index f9ed130db49..4eb5f6dd044 100644 --- a/packages/itwinui-react/src/core/Tooltip/Tooltip.tsx +++ b/packages/itwinui-react/src/core/Tooltip/Tooltip.tsx @@ -21,7 +21,7 @@ import { inline, useDelayGroup, } from '@floating-ui/react'; -import type { Placement } from '@floating-ui/react'; +import type { FloatingDelayGroup, Placement } from '@floating-ui/react'; import { Box, Portal, @@ -37,6 +37,13 @@ import type { // ---------------------------------------------------------------------------- +export const defaultTooltipDelay: React.ComponentPropsWithoutRef< + typeof FloatingDelayGroup +>['delay'] = { + open: 100, + close: 200, +}; + type TooltipOptions = { /** * Placement of the Tooltip @@ -197,7 +204,7 @@ const useTooltip = (options: TooltipOptions = {}) => { const interactions = useInteractions([ useHover(floating.context, { - delay: delay !== 0 ? delay : { open: 50, close: 250 }, // If FloatingDelayGroup exists, use its delay + delay: delay !== 0 ? delay : defaultTooltipDelay, // If FloatingDelayGroup exists, use its delay handleClose: safePolygon({ buffer: -Infinity }), move: false, }),