diff --git a/.storybook/preview.scss b/.storybook/preview.scss index 31d4152..e7a2284 100644 --- a/.storybook/preview.scss +++ b/.storybook/preview.scss @@ -1 +1,2 @@ @import "vanilla-framework"; +@include vanilla; diff --git a/package-lock.json b/package-lock.json index 09123a0..3038871 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,6 +61,7 @@ "classnames": "^2.3.2", "react": "18.2.0", "react-dom": "18.2.0", + "react-router-dom": "^6.0.0", "vanilla-framework": "^4.3.0" } }, diff --git a/package.json b/package.json index a992752..44ab709 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "@types/react-dom": "^17.0.2 || ^18.0.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-router-dom": "^6.0.0", "vanilla-framework": "^4.3.0" } } diff --git a/src/lib/elements/ExternalLink/ExternalLink.stories.tsx b/src/lib/elements/ExternalLink/ExternalLink.stories.tsx new file mode 100644 index 0000000..6e1bbf5 --- /dev/null +++ b/src/lib/elements/ExternalLink/ExternalLink.stories.tsx @@ -0,0 +1,17 @@ +import type { Meta } from "@storybook/react"; + +import { ExternalLink } from "."; + +const meta: Meta = { + title: "Components/ExternalLink", + component: ExternalLink, + tags: ["autodocs"], +}; +export default meta; + +export const Example = { + args: { + children: "maas.io", + to: "https://maas.io", + }, +}; diff --git a/src/lib/elements/ExternalLink/ExternalLink.test.tsx b/src/lib/elements/ExternalLink/ExternalLink.test.tsx new file mode 100644 index 0000000..2bb1b1d --- /dev/null +++ b/src/lib/elements/ExternalLink/ExternalLink.test.tsx @@ -0,0 +1,30 @@ +import { render, screen } from "@testing-library/react"; +import { userEvent } from "@testing-library/user-event"; +import { vi } from "vitest"; + +import { ExternalLink } from "./ExternalLink"; + +test("renders with correct attributes", () => { + render(Example Link); + + const linkElement = screen.getByText(/example link/i); + expect(linkElement).toBeInTheDocument(); + expect(linkElement).toHaveAttribute("rel", "noreferrer noopener"); + expect(linkElement).toHaveAttribute("href", "https://example.com"); + expect(linkElement).toHaveAttribute("target", "_blank"); +}); + +test("calls onClick handler when pressed", async () => { + const handleClick = vi.fn(); + + render( + + Example Link + , + ); + + const linkElement = screen.getByText(/example link/i); + await userEvent.click(linkElement); + + expect(handleClick).toHaveBeenCalled(); +}); diff --git a/src/lib/elements/ExternalLink/ExternalLink.tsx b/src/lib/elements/ExternalLink/ExternalLink.tsx new file mode 100644 index 0000000..0a20602 --- /dev/null +++ b/src/lib/elements/ExternalLink/ExternalLink.tsx @@ -0,0 +1,10 @@ +import { Link, LinkProps } from "@canonical/react-components"; +import type { LinkProps as RouterLinkProps } from "react-router-dom"; + +type ExternalLinkProps = Pick & + Omit & { to: string }; +export const ExternalLink = ({ children, to, ...props }: ExternalLinkProps) => ( + + {children} + +); diff --git a/src/lib/elements/ExternalLink/index.ts b/src/lib/elements/ExternalLink/index.ts new file mode 100644 index 0000000..7c091aa --- /dev/null +++ b/src/lib/elements/ExternalLink/index.ts @@ -0,0 +1 @@ +export * from "./ExternalLink"; diff --git a/src/lib/elements/index.ts b/src/lib/elements/index.ts index 802a10b..1775515 100644 --- a/src/lib/elements/index.ts +++ b/src/lib/elements/index.ts @@ -1 +1,2 @@ export * from "./Meter"; +export * from "./ExternalLink"; diff --git a/vite.config.ts b/vite.config.ts index c833546..ba6c6c1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -30,12 +30,15 @@ export default defineConfig({ "react/jsx-runtime", "react-dom", "vanilla-framework", + "react-router-dom", ], output: { globals: { react: "React", + "@canonical/react-components": "@canonical/react-components", "react/jsx-runtime": "react/jsx-runtime", "react-dom": "ReactDOM", + "react-router-dom": "react-router-dom", }, }, },