diff --git a/src/components/Form/Controls/Action/Action.stories.tsx b/src/components/Form/Controls/Action/Action.stories.tsx index d6936264..06f64efc 100644 --- a/src/components/Form/Controls/Action/Action.stories.tsx +++ b/src/components/Form/Controls/Action/Action.stories.tsx @@ -31,6 +31,7 @@ const icons = { }; import { ActionInput } from "./"; +import { TooltipProvider } from "../../../Tooltip/TooltipProvider"; type Props = { invalid?: boolean } & React.ComponentProps; @@ -88,7 +89,9 @@ export default { }, }, render: ({ invalid, ...restArgs }) => ( - + + + ), args: { placeholder: "", diff --git a/src/components/Form/Controls/Action/Action.test.tsx b/src/components/Form/Controls/Action/Action.test.tsx index 529c5b49..3ee66325 100644 --- a/src/components/Form/Controls/Action/Action.test.tsx +++ b/src/components/Form/Controls/Action/Action.test.tsx @@ -20,17 +20,20 @@ import React from "react"; import ChatIcon from "@vector-im/compound-design-tokens/icons/chat.svg"; import { ActionInput } from "./Action"; +import { TooltipProvider } from "../../../Tooltip/TooltipProvider"; describe("ActionInput", () => { it("renders", () => { const { asFragment } = render( - { - console.log("clicked!"); - }} - />, + + { + console.log("clicked!"); + }} + /> + , ); expect(asFragment()).toMatchSnapshot(); }); @@ -39,11 +42,13 @@ describe("ActionInput", () => { const spy = vi.fn(); const { container } = render( - , + + + , ); const actionBtn = getByLabelText(container, "Click me!"); diff --git a/src/components/Form/Controls/Password/Password.stories.tsx b/src/components/Form/Controls/Password/Password.stories.tsx index 70e970b4..97ecbb73 100644 --- a/src/components/Form/Controls/Password/Password.stories.tsx +++ b/src/components/Form/Controls/Password/Password.stories.tsx @@ -22,6 +22,7 @@ import { Meta, StoryObj } from "@storybook/react"; import { PasswordInput } from "./"; import { within } from "@storybook/testing-library"; import { userEvent } from "@storybook/testing-library"; +import { TooltipProvider } from "../../../Tooltip/TooltipProvider"; type Props = { invalid?: boolean } & React.ComponentProps; @@ -62,7 +63,9 @@ export default { }, }, render: ({ invalid, ...restArgs }) => ( - + + + ), args: { placeholder: "", diff --git a/src/components/Form/Controls/Password/Password.test.tsx b/src/components/Form/Controls/Password/Password.test.tsx index 64a4b2e6..872599d8 100644 --- a/src/components/Form/Controls/Password/Password.test.tsx +++ b/src/components/Form/Controls/Password/Password.test.tsx @@ -20,10 +20,15 @@ import { act, getByLabelText, render } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { PasswordInput } from "./Password"; +import { TooltipProvider } from "../../../Tooltip/TooltipProvider"; describe("PasswordControl", () => { it("switches the input type", async () => { - const { container } = render(); + const { container } = render( + + + , + ); expect(container.querySelector("[type=password]")).toBeInTheDocument(); expect(container).toMatchSnapshot("invisible"); diff --git a/src/components/Tooltip/Tooltip.stories.tsx b/src/components/Tooltip/Tooltip.stories.tsx index a32ed3e6..ed1dfe70 100644 --- a/src/components/Tooltip/Tooltip.stories.tsx +++ b/src/components/Tooltip/Tooltip.stories.tsx @@ -14,13 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React, { FC, ReactNode } from "react"; import { Meta, StoryFn } from "@storybook/react"; import { Tooltip as TooltipComponent } from "./Tooltip"; import { IconButton } from "../Button"; import UserIcon from "@vector-im/compound-design-tokens/icons/user-profile.svg"; +import { TooltipProvider } from "./TooltipProvider"; export default { title: "Tooltip", @@ -72,14 +73,20 @@ export default { }, decorators: [ (Story: StoryFn) => ( -
- -
+ +
+ +
+
), ], } as Meta; -const TemplateSide: StoryFn = () => ( +interface LayoutProps { + children: ReactNode; +} + +const Layout: FC = ({ children }) => (
= () => ( alignItems: "center", }} > - - - - - - - - - - - - - - - - - - - - + {children}
); +const TemplateSide: StoryFn = () => ( + + {(["top", "right", "bottom", "left"] as const).map((side) => ( + + + + + + ))} + +); + export const Side = TemplateSide.bind({}); Side.args = {}; const TemplateAlign: StoryFn = () => ( -
+ - - - - - - - - - - -
+ {(["start", "end"] as const).map((align) => ( + + + + + + ))} + ); export const Align = TemplateAlign.bind({}); diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index beb568ca..fe09a097 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -15,14 +15,7 @@ limitations under the License. */ import React, { PropsWithChildren } from "react"; -import { - Root, - Trigger, - Portal, - Content, - Arrow, - Provider, -} from "@radix-ui/react-tooltip"; +import { Root, Trigger, Portal, Content, Arrow } from "@radix-ui/react-tooltip"; import styles from "./Tooltip.module.css"; import classNames from "classnames"; @@ -101,37 +94,35 @@ export const Tooltip = ({ open, }: PropsWithChildren): JSX.Element => { return ( - - - - {isTriggerInteractive ? ( - children - ) : ( - {children} + + + {isTriggerInteractive ? ( + children + ) : ( + {children} + )} + + + + {label} + {/* 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 */} + {caption && ( + + {caption} + )} - - - - {label} - {/* 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 */} - {caption && ( - - {caption} - - )} - - - - - + + + + ); }; diff --git a/src/components/Tooltip/TooltipProvider.tsx b/src/components/Tooltip/TooltipProvider.tsx new file mode 100644 index 00000000..b41ca95e --- /dev/null +++ b/src/components/Tooltip/TooltipProvider.tsx @@ -0,0 +1,28 @@ +/* +Copyright 2024 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { Provider } from "@radix-ui/react-tooltip"; +import { FC, ReactNode } from "react"; + +interface TooltipProviderProps { + children: ReactNode; +} + +/** + * Provides global functionality to your tooltips. You must wrap your + * application in this component for tooltips to function. + */ +export const TooltipProvider: FC = Provider; diff --git a/src/index.ts b/src/index.ts index 1a3b1aca..33bc0fde 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,6 +43,7 @@ export { Search } from "./components/Search/Search"; export { Separator } from "./components/Separator/Separator"; export { ToggleMenuItem } from "./components/Menu/ToggleMenuItem"; export { Tooltip } from "./components/Tooltip/Tooltip"; +export { TooltipProvider } from "./components/Tooltip/TooltipProvider"; export { TextControl,