Skip to content

Commit

Permalink
feat(tooltip): implement onOpenChange$
Browse files Browse the repository at this point in the history
  • Loading branch information
cwoolum committed Aug 1, 2024
1 parent 74dd6cd commit ad7e538
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { component$, useSignal } from '@builder.io/qwik';
import { Tooltip } from '@qwik-ui/headless';

export default component$(() => {
const tooltipState = useSignal<'open' | 'closed'>('closed');

return (
<>
<Tooltip.Root gutter={4} onOpenChange$={(e) => (tooltipState.value = e)} flip>
<Tooltip.Trigger>Hover or Focus me</Tooltip.Trigger>
<Tooltip.Panel aria-label="Tooltip content">
<Tooltip.Arrow width={10} height={5} />
Tooltip content here
</Tooltip.Panel>
</Tooltip.Root>
The tooltip is {tooltipState.value}
</>
);
});
13 changes: 13 additions & 0 deletions apps/website/src/routes/docs/headless/tooltip/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ CSS from the example:

<CodeSnippet name="transition.css" />

## Events

The tooltip contains a `onOpenChange$` event that runs when the tooltip opens or closes.
This can be used to trigger additional actions when the tooltip is opened or closed.

<Showcase name="onChange" />

## Additional References

Qwik UI aims to be in line with the standard whenever possible. Our goal is to empower Qwik developers to create amazing experiences for their users.
Expand Down Expand Up @@ -255,6 +262,12 @@ To read more about tooltips you can check it out on:
type: 'selector',
description: 'Style the element when the tooltip is open.',
},
{
name: 'onOpenChange$',
type: 'QRL',
description: 'QRL handler that runs when the tooltip opens or closes.',
info: 'QRL<(state: "open" | "closed") => void>',
},

]}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContextId, Signal } from '@builder.io/qwik';
import { createContextId, QRL, Signal } from '@builder.io/qwik';

export const TooltipContextId = createContextId<TooltipContext>('Tooltip');

Expand All @@ -11,6 +11,8 @@ export type TooltipContext = {
triggerRef: Signal<HTMLButtonElement | undefined>;

state: Signal<TriggerDataState>;

onOpenChange$: QRL<(state: 'open' | 'closed') => void>;
};

export type TriggerDataState = 'closing' | 'closed' | 'opening' | 'open';
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ export const HTooltipPanel = component$((props: HTooltipPanelProps) => {
const context = useContext(TooltipContextId);

return (
<HPopoverPanel {...props} role="tooltip" id={context.localId}>
<HPopoverPanel
{...props}
role="tooltip"
onToggle$={(e) => context.onOpenChange$(e.newState)}
id={context.localId}
>
<Slot />
</HPopoverPanel>
);
Expand Down
6 changes: 4 additions & 2 deletions packages/kit-headless/src/components/tooltip/tooltip-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
useContextProvider,
useId,
useSignal,
$,
} from '@builder.io/qwik';
import { FloatingProps, HPopoverRoot } from '../popover/popover-root';
import { TooltipContext, TooltipContextId, TriggerDataState } from './tooltip-context';
Expand All @@ -27,7 +28,7 @@ export type TooltipRootProps = {
* QRL handler that runs when the tooltip opens or closes.
* @param open The new state of the tooltip.
*/
onOpenChange$?: QRL<(state: TriggerDataState) => void>;
onOpenChange$?: QRL<(state: 'open' | 'closed') => void>;

/**
* A value that determines how long before the tooltip will
Expand Down Expand Up @@ -58,7 +59,7 @@ export const HTooltipRoot = component$((props: TooltipProps) => {
gutter,
delayDuration = 0,
flip,
onOpenChange$: _,
onOpenChange$,
...rest
} = props;

Expand All @@ -74,6 +75,7 @@ export const HTooltipRoot = component$((props: TooltipProps) => {
triggerRef,
delayDuration,
state: tooltipState,
onOpenChange$: $((e) => onOpenChange$?.(e)),
};

useContextProvider(TooltipContextId, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export function createTooltipDriver<T extends DriverLocator>(rootLocator: T) {
return getTrigger().all();
};

const getOnChangeVerificationText = (state: 'open' | 'closed') => {
return rootLocator.getByText(`The tooltip is ${state}`);
};

const getProgrammaticButtonTrigger = () => {
return rootLocator.locator('button');
};
Expand All @@ -65,5 +69,6 @@ export function createTooltipDriver<T extends DriverLocator>(rootLocator: T) {
openTooltip,
getProgrammaticButtonTrigger,
getTooltipByTextContent,
getOnChangeVerificationText,
};
}
17 changes: 17 additions & 0 deletions packages/kit-headless/src/components/tooltip/tooltip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,20 @@ test.describe('Tooltip Animations', () => {
await expect(tooltip).toBeHidden();
});
});

test.describe('Tooltip Events', () => {
test(`GIVEN a tooltip with opOpenChange configured
WHEN hovering over the trigger
THEN the text should say "The tooltip is open"`, async ({ page }) => {
const { driver: d } = await setup(page, 'onChange');
const tooltip = d.getTooltip();
const trigger = d.getTrigger();

expect(d.getOnChangeVerificationText('closed')).toBeVisible();

await trigger.hover();
await expect(tooltip).toBeVisible();

expect(d.getOnChangeVerificationText('open')).toBeVisible();
});
});

0 comments on commit ad7e538

Please sign in to comment.