import { expect, fn, userEvent, within } from "storybook/test"; import type { Meta, StoryObj } from "@storybook/react-vite"; import { FolderRegularIcon } from "alouette-icons/phosphor-icons/Folder"; import type { ReactNode } from "react"; import type { Accent } from "../../core/AlouetteConfig"; import { Paragraph } from "../primitives/Text"; import { View } from "../primitives/View"; import { Story, accentsWithoutNeutral, neutralAndAccents, } from "../story-components/Story"; import { LinkText } from "./LinkText"; type ThisStory = StoryObj; export default { title: "alouette/Actions/LinkText", component: LinkText, parameters: { componentSubtitle: "Text link to an in-app destination — the lightweight alternative to a Button when the link is part of a text flow", docs: { description: { component: `Renders a real \`\` on web, which navigates on its own; native ignores the href and routes from \`onPress\`, so expo Router's \`\` — it injects both — composes with it directly. Use \`ExternalLinkText\` for a destination outside the app, and a \`NavBar\` for the primary navigation between screens. ### Usage ~~~tsx } /> ~~~`, }, }, }, argTypes: { size: { control: "inline-radio", options: ["sm", "md"] }, accent: { control: "select", options: neutralAndAccents }, disabled: { control: "boolean" }, text: { control: "text" }, }, } satisfies Meta; export const LinkTextPreviewStory: ThisStory = { name: "LinkText Preview", parameters: { layout: "padded", chromatic: { disableSnapshot: true }, }, args: { href: "/library", text: "Back to the library" }, }; function AccentLinkText({ accent }: { accent?: Accent }): ReactNode { return ( } text={`${accent ?? "default"} with icon`} /> ); } export const LinkTextVariantsStory: ThisStory = { name: "LinkText Variants", render: () => ( } size="sm" text="Small link with an icon" /> } text="Medium link with an icon" /> } text="Disabled link with an icon" /> The report you are looking for moved to the shared library. } size="sm" text="Open the library" /> {"The report you are looking for moved to the "} , next to the other documents shared with your team during the last quarter. {"Every document of the quarter now sits in "} } size="sm" text="reports" /> , sorted by date, with the drafts kept apart until they are reviewed. {accentsWithoutNeutral.map((accent) => ( ))} ), }; export const LinkTextTestsStory: ThisStory = { name: "LinkText Tests", render: () => ( { event.preventDefault(); }} /> ), play: async ({ canvasElement }) => { const canvas = within(canvasElement); const library = canvas.getByRole("link", { name: "Library" }); await expect(library.tagName).toBe("A"); await expect(library).toHaveAttribute("href", "/library"); // A disabled Pressable never sees the press that would cancel the // navigation, so the href goes with it. const reports = canvas.getByRole("link", { name: "Reports" }); await expect(reports).toHaveAttribute("aria-disabled", "true"); await expect(reports).not.toHaveAttribute("href"); const settings = canvas.getByRole("link", { name: "Settings" }); settings.focus(); await userEvent.tab({ shift: true }); await userEvent.tab(); await expect(document.activeElement).toBe(settings); await expect(getComputedStyle(settings).outlineWidth).toBe("2px"); }, };