Skip to content

Commit

Permalink
Merge pull request #165 from element-hq/florianduros/tooltip-caption
Browse files Browse the repository at this point in the history
Allow component for tooltip caption
  • Loading branch information
florianduros authored May 7, 2024
2 parents bf62ac7 + ba16402 commit ca0da2c
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 24 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 14 additions & 2 deletions src/components/Tooltip/Tooltip.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,23 @@ export const Default = {
},
};

export const WithCaption = {
export const WithStringCaption = {
args: {
...Default.args,
label: "I can have a caption",
caption: "My beautiful caption",
},
};

export const WithComponentCaption = {
args: {
...Default.args,
label: "Copy",
caption: "Ctrl + C",
caption: (
<>
<kbd>Ctrl</kbd> + <kbd>C</kbd>
</>
),
},
};

Expand Down
17 changes: 14 additions & 3 deletions src/components/Tooltip/Tooltip.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import { composeStories, composeStory } from "@storybook/react";

const {
Default,
WithCaption,
WithStringCaption,
WithComponentCaption,
ForcedOpen,
ForcedClose,
InteractiveTrigger,
Expand Down Expand Up @@ -92,8 +93,18 @@ describe("Tooltip", () => {
expect(container).toMatchSnapshot();
});

it("renders with caption", async () => {
const { asFragment } = render(<WithCaption />);
it("renders with string caption", async () => {
const { asFragment } = render(<WithStringCaption />);
expect(asFragment()).toMatchSnapshot();
const trigger = screen.getByTestId("testbutton");

fireEvent.focus(trigger);
// tooltip shown
expect(await screen.findByRole("tooltip")).toMatchSnapshot();
});

it("renders with component caption", async () => {
const { asFragment } = render(<WithComponentCaption />);
expect(asFragment()).toMatchSnapshot();
const trigger = screen.getByTestId("testbutton");

Expand Down
36 changes: 24 additions & 12 deletions src/components/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,23 +87,35 @@ export function Tooltip({
</TooltipAnchor>
<TooltipContent>
<span id={context.labelId}>{label}</span>
{/* Forcing dark theme, so that we have the correct contrast when
using the text color secondary on a solid dark background.
This is temporary and should only remain until we figure out
the approach to on-solid tokens */}
{props.caption && (
<span
id={context.captionId}
className={classNames(styles.caption, "cpd-theme-dark")}
>
{props.caption}
</span>
)}
<Caption />
</TooltipContent>
</TooltipContext.Provider>
);
}

function Caption() {
const { caption, captionId } = useTooltipContext();
if (!caption) return null;

const isCaptionString = typeof caption === "string";
const Container = isCaptionString ? "span" : "div";

/**
* Forcing dark theme, so that we have the correct contrast when
* using the text color secondary on a solid dark background.
* This is temporary and should only remain until we figure out
* the approach to on-solid tokens
**/
return (
<Container
id={captionId}
className={classNames(styles.caption, "cpd-theme-dark")}
>
{caption}
</Container>
);
}

/**
* The content of the tooltip
* @param children
Expand Down
101 changes: 97 additions & 4 deletions src/components/Tooltip/__snapshots__/Tooltip.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ exports[`Tooltip > renders open by default 2`] = `
</div>
`;

exports[`Tooltip > renders with caption 1`] = `
exports[`Tooltip > renders with component caption 1`] = `
<DocumentFragment>
<div
style="padding: 100px;"
Expand Down Expand Up @@ -347,7 +347,100 @@ exports[`Tooltip > renders with caption 1`] = `
</DocumentFragment>
`;

exports[`Tooltip > renders with caption 2`] = `
exports[`Tooltip > renders with component caption 2`] = `
<div
aria-describedby=":r17:"
aria-labelledby=":r16:"
class="_tooltip_9b26f8"
id=":r18:"
role="tooltip"
style="position: absolute; left: 0px; top: 0px; transform: translate(5px, 6px);"
tabindex="-1"
>
<svg
aria-hidden="true"
class="_arrow_9b26f8"
height="10"
style="position: absolute; pointer-events: none; bottom: 100%; transform: rotate(180deg); left: -1px;"
viewBox="0 0 10 10"
width="10"
>
<path
d="M0,0 H10 L5,6 Q5,6 5,6 Z"
stroke="none"
/>
<clippath
id=":r1b:"
>
<rect
height="10"
width="10"
x="0"
y="0"
/>
</clippath>
</svg>
<span
id=":r16:"
>
Copy
</span>
<div
class="_caption_9b26f8 cpd-theme-dark"
id=":r17:"
>
<kbd>
Ctrl
</kbd>
+
<kbd>
C
</kbd>
</div>
</div>
`;

exports[`Tooltip > renders with string caption 1`] = `
<DocumentFragment>
<div
style="padding: 100px;"
>
<button
class="_icon-button_e3253e"
data-testid="testbutton"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_ab1fc6"
style="--cpd-icon-button-size: 100%;"
>
<svg
class="cpd-icon"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.175 13.825C9.958 14.608 10.9 15 12 15s2.042-.392 2.825-1.175C15.608 13.042 16 12.1 16 11s-.392-2.042-1.175-2.825C14.042 7.392 13.1 7 12 7s-2.042.392-2.825 1.175C8.392 8.958 8 9.9 8 11s.392 2.042 1.175 2.825Zm4.237-1.412A1.926 1.926 0 0 1 12 13c-.55 0-1.02-.196-1.412-.588A1.926 1.926 0 0 1 10 11c0-.55.196-1.02.588-1.412A1.926 1.926 0 0 1 12 9c.55 0 1.02.196 1.412.588.392.391.588.862.588 1.412 0 .55-.196 1.02-.588 1.412Z"
/>
<path
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10Zm-2 0a8 8 0 1 0-16 0 8 8 0 0 0 16 0Z"
/>
<path
d="M16.23 18.792a12.47 12.47 0 0 0-1.455-.455 11.6 11.6 0 0 0-5.55 0c-.487.12-.972.271-1.455.455a8.04 8.04 0 0 1-1.729-1.454c.89-.412 1.794-.729 2.709-.95A13.76 13.76 0 0 1 12 16c1.1 0 2.183.13 3.25.387a14.78 14.78 0 0 1 2.709.95 8.042 8.042 0 0 1-1.73 1.455Z"
/>
</svg>
</div>
</button>
</div>
</DocumentFragment>
`;

exports[`Tooltip > renders with string caption 2`] = `
<div
aria-describedby=":r11:"
aria-labelledby=":r10:"
Expand Down Expand Up @@ -383,13 +476,13 @@ exports[`Tooltip > renders with caption 2`] = `
<span
id=":r10:"
>
Copy
I can have a caption
</span>
<span
class="_caption_9b26f8 cpd-theme-dark"
id=":r11:"
>
Ctrl + C
My beautiful caption
</span>
</div>
`;
9 changes: 6 additions & 3 deletions src/components/Tooltip/useTooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
useInteractions,
useRole,
} from "@floating-ui/react";
import { useMemo, useRef, useState } from "react";
import { useMemo, useRef, useState, JSX } from "react";

interface UseTooltipProps {
/**
Expand All @@ -51,8 +51,10 @@ interface UseTooltipProps {
placement: Placement;
/**
* The caption of the tooltip.
* JSX.Element can be used to provide accessibility content like kbd element.
* Keep in mind, the caption should not be used for interactive content.
*/
caption?: string;
caption?: string | JSX.Element;
/**
* The event handler for the open change.
*/
Expand Down Expand Up @@ -141,12 +143,13 @@ export function useTooltip({
() => ({
labelId,
captionId: caption ? captionId : undefined,
caption,
open,
setOpen,
...interactions,
...data,
arrowRef,
}),
[labelId, captionId, open, setOpen, interactions, data, arrowRef],
[labelId, captionId, caption, open, setOpen, interactions, data, arrowRef],
);
}

0 comments on commit ca0da2c

Please sign in to comment.