Skip to content

Commit 6381646

Browse files
authored
Merge pull request #528 from element-hq/dbkr/iconbutton_tooltipopenchange
Add onTooltipOpenChange handler to IconButton
2 parents d750e42 + 17f7574 commit 6381646

2 files changed

Lines changed: 44 additions & 3 deletions

File tree

src/components/Button/IconButton/IconButton.test.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
55
Please see LICENSE files in the repository root for full details.
66
*/
77

8-
import { describe, it, expect } from "vitest";
9-
import { render } from "@testing-library/react";
8+
import { describe, it, expect, vi } from "vitest";
9+
import { render, screen } from "@testing-library/react";
1010
import React from "react";
1111
import { composeStories } from "@storybook/react";
12+
import userEvent from "@testing-library/user-event";
1213

1314
import * as stories from "./IconButton.stories";
15+
import { IconButton } from "./IconButton";
16+
import { TooltipProvider } from "../../Tooltip/TooltipProvider";
17+
import { UserIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
1418

1519
const {
1620
Default,
@@ -56,4 +60,30 @@ describe("IconButton", () => {
5660
const { container } = render(<WithSecondaryKindAndNoBackground />);
5761
expect(container).toMatchSnapshot();
5862
});
63+
it("calls onTooltipOpenChange when the tooltip opens and closes", async () => {
64+
const user = userEvent.setup();
65+
const onTooltipOpenChange = vi.fn();
66+
render(
67+
<TooltipProvider>
68+
<IconButton tooltip="Profile" onTooltipOpenChange={onTooltipOpenChange}>
69+
<UserIcon />
70+
</IconButton>
71+
</TooltipProvider>,
72+
);
73+
74+
await user.tab();
75+
expect(screen.getByRole("button")).toHaveFocus();
76+
expect(onTooltipOpenChange).toHaveBeenLastCalledWith(
77+
true,
78+
expect.anything(),
79+
expect.anything(),
80+
);
81+
82+
await user.tab();
83+
expect(onTooltipOpenChange).toHaveBeenLastCalledWith(
84+
false,
85+
expect.anything(),
86+
expect.anything(),
87+
);
88+
});
5989
});

src/components/Button/IconButton/IconButton.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import styles from "./IconButton.module.css";
1212
import { UnstyledButton, type UnstyledButtonPropsFor } from "../UnstyledButton";
1313
import { IndicatorIcon } from "../../Icon/IndicatorIcon/IndicatorIcon";
1414
import { Tooltip } from "../../Tooltip/Tooltip";
15+
import type { OpenChangeReason } from "@floating-ui/react";
1516

1617
type IconButtonProps = UnstyledButtonPropsFor<"button"> & {
1718
/**
@@ -51,6 +52,11 @@ type IconButtonProps = UnstyledButtonPropsFor<"button"> & {
5152
* The placement of the tooltip, if `tooltip` is provided.
5253
*/
5354
tooltipPlacement?: React.ComponentProps<typeof Tooltip>["placement"];
55+
onTooltipOpenChange?: (
56+
open: boolean,
57+
event?: Event | undefined,
58+
reason?: OpenChangeReason | undefined,
59+
) => void;
5460
/**
5561
* Hide the background when the button is not active or hovered.
5662
* @default false
@@ -77,6 +83,7 @@ export const IconButton = forwardRef<
7783
tooltip,
7884
tooltipPlacement,
7985
noBackground = false,
86+
onTooltipOpenChange,
8087
...props
8188
},
8289
ref,
@@ -112,7 +119,11 @@ export const IconButton = forwardRef<
112119
);
113120

114121
return tooltip ? (
115-
<Tooltip label={tooltip} placement={tooltipPlacement}>
122+
<Tooltip
123+
label={tooltip}
124+
placement={tooltipPlacement}
125+
onOpenChange={onTooltipOpenChange}
126+
>
116127
{button}
117128
</Tooltip>
118129
) : (

0 commit comments

Comments
 (0)