Skip to content

Commit

Permalink
✨ - feat: implemented the tooltip component
Browse files Browse the repository at this point in the history
  • Loading branch information
Xaohs authored and svenvandescheur committed Feb 8, 2024
1 parent a0c7f56 commit 2021471
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export * from "./page";
export * from "./paginator";
export * from "./tabs";
export * from "./toolbar";
export * from "./tooltip";
export * from "./typography";
1 change: 1 addition & 0 deletions src/components/tooltip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./tooltip";
35 changes: 35 additions & 0 deletions src/components/tooltip/tooltip.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.mykn-tooltip {
transition: opacity 0.5s;
transform: scale(0.8);
opacity: 0;
border-radius: var(--border-radius-m);

&--open {
opacity: 1;
background-color: var(--theme-color-primary-200);
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
transform: scale(1);
}

&__arrow {
fill: var(--theme-color-tertiary-400);
}

&__content {
padding: var(--spacing-h-m) var(--spacing-v-m);
box-sizing: border-box;
overflow-wrap: break-word;

&--sm {
max-width: 200px;
}

&--md {
max-width: 400px;
}

&--lg {
max-width: 600px;
}
}
}
97 changes: 97 additions & 0 deletions src/components/tooltip/tooltip.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";

import { Button } from "../button";
import { Outline } from "../icon";
import { Tooltip } from "./tooltip";

const meta = {
title: "Controls/Tooltip",
component: Tooltip,
decorators: [
(Story) => (
<div style={{ padding: "2rem" }}>
<Story />
</div>
),
],
} satisfies Meta<typeof Tooltip>;

export default meta;
type Story = StoryObj<typeof meta>;

export const TooltipComponent: Story = {
args: {
children: (
<Button variant="transparent">
<Outline.InformationCircleIcon>Hover me</Outline.InformationCircleIcon>
</Button>
),
content:
"This tooltip works by hovering over any react element, and it can be placed in any direction.",
},
};

export const TooltipTop: Story = {
args: {
...TooltipComponent.args,
placement: "top",
},
decorators: [
(Story) => (
<div style={{ paddingTop: "4rem" }}>
<Story />
</div>
),
],
};

export const TooltipRight: Story = {
args: {
...TooltipComponent.args,
placement: "right",
},
};

export const TooltipBottom: Story = {
args: {
...TooltipComponent.args,
placement: "bottom",
},
};

export const TooltipLeft: Story = {
args: {
...TooltipComponent.args,
placement: "left",
},
decorators: [
(Story) => (
<div style={{ display: "flex", justifyContent: "end" }}>
<Story />
</div>
),
],
};

export const TooltipBigText: Story = {
args: {
...TooltipComponent.args,
content: (
<div>
<p>
This tooltip works by hovering over any react element, and it can be
placed in any direction.
</p>
<p>
This tooltip works by hovering over any react element, and it can be
placed in any direction.
</p>
<p>
This tooltip works by hovering over any react element, and it can be
placed in any direction.
</p>
</div>
),
},
};
114 changes: 114 additions & 0 deletions src/components/tooltip/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import {
FloatingArrow,
Placement,
arrow,
autoUpdate,
flip,
offset,
shift,
useDismiss,
useFloating,
useFocus,
useHover,
useInteractions,
useRole,
useTransitionStyles,
} from "@floating-ui/react";
import clsx from "clsx";
import React, { ReactNode, useRef, useState } from "react";

import { P } from "../typography";
import "./tooltip.scss";

type TooltipProps = React.PropsWithChildren<{
/* The content to display in the tooltip */
content?: ReactNode;

/* The placement of the tooltip */
placement?: Placement;

/* The size of the tooltip, defaults to md */
size?: "sm" | "md" | "lg";
}>;

export const Tooltip = ({
content,
children,
placement,
size = "md",
}: TooltipProps) => {
const [isOpen, setIsOpen] = useState(false);
const arrowRef = useRef(null);

const {
refs: { setReference, setFloating },
floatingStyles,
context,
} = useFloating({
open: isOpen,
onOpenChange: setIsOpen,
placement,
middleware: [
offset(12),
flip(),
shift(),
arrow({
element: arrowRef,
}),
],
whileElementsMounted: autoUpdate,
});

const hover = useHover(context, { move: false });
const focus = useFocus(context);
const dismiss = useDismiss(context);
const role = useRole(context, { role: "tooltip" });

const { getReferenceProps, getFloatingProps } = useInteractions([
hover,
focus,
dismiss,
role,
]);

const { styles: transitionStyles } = useTransitionStyles(context, {
initial: {
opacity: 0,
transform: "scale(0.8)",
},
});

return (
<>
{React.cloneElement(children as React.ReactElement, {
...getReferenceProps(),
ref: setReference,
})}
<div
ref={setFloating}
style={{ ...floatingStyles, zIndex: 1 }}
{...getFloatingProps({
className: clsx("mykn-tooltip", {
"mykn-tooltip--open": isOpen,
}),
})}
>
<div
style={transitionStyles}
className={clsx("mykn-tooltip__content", {
"mykn-tooltip__content--sm": size === "sm",
"mykn-tooltip__content--md": size === "md",
"mykn-tooltip__content--lg": size === "lg",
})}
>
<FloatingArrow
ref={arrowRef}
context={context}
className="mykn-tooltip__arrow"
/>
<P size="xs">{content}</P>
</div>
</div>
</>
);
};

0 comments on commit 2021471

Please sign in to comment.