Skip to content

Commit

Permalink
Merge pull request #10 from jtiala/anchor
Browse files Browse the repository at this point in the history
Add anchor
  • Loading branch information
jtiala authored Jul 31, 2023
2 parents 826a8ed + dd9c95e commit bc52a47
Show file tree
Hide file tree
Showing 24 changed files with 323 additions and 51 deletions.
8 changes: 7 additions & 1 deletion apps/docs/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ const preview: Preview = {
parameters: {
options: {
storySort: {
order: ["Introduction", "Development", "Typography", "Form"],
order: [
"Introduction",
"Development",
"Typography",
"Navigation",
"Form",
],
},
},
actions: { argTypesRegex: "^on[A-Z].*" },
Expand Down
29 changes: 29 additions & 0 deletions apps/docs/stories/Navigation/Anchor.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Anchor } from "@themeless-ui/react";

const meta = {
title: "Navigation/Anchor",
component: Anchor,
tags: ["autodocs"],
} satisfies Meta<typeof Anchor>;

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

const href = "https://example.com";
const children = "Example.com";

export const Default: Story = {
args: {
href,
children,
},
};

export const ExternalLink: Story = {
args: {
href,
target: "_blank",
children,
},
};
8 changes: 4 additions & 4 deletions apps/docs/stories/Typography/Text.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,12 @@ export const CombiningTextTypes = {
<li>
<Description>del and ins in cite</Description>
<Text>
First verse of{" "}
Lorem ipsum{" "}
<Text type="cite">
<Text type="del">Think</Text>
<Text type="ins">Respect</Text>
<Text type="del">dolor</Text>
<Text type="ins">sit amet</Text>
</Text>
, by Aretha Franklin.
, consectetur adipiscing elit.
</Text>
</li>
<li>
Expand Down
24 changes: 13 additions & 11 deletions apps/nextjs-example/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Heading, Text } from "@themeless-ui/react";
import { Anchor, Heading, Text } from "@themeless-ui/react";
import type { Metadata } from "next";
import Link from "next/link";
import "./index.css";
Expand All @@ -22,31 +22,33 @@ export default function RootLayout({
<Heading level={2}>ThemelessUI Next.js Example App</Heading>
<nav>
<Text>
{/* TODO: use <Anchor> */}
<Link href="/">Home</Link>
<Link href="/" passHref legacyBehavior>
<Anchor>Home</Anchor>
</Link>
{" | "}
<Link href="/about">About</Link>
<Link href="/about" passHref legacyBehavior>
<Anchor>About</Anchor>
</Link>
</Text>
</nav>
</header>
<main>{children}</main>
<footer>
<Text type="small">
{/* TODO: use <Anchor> */}
<Link
href="https://github.com/jtiala/themeless-ui"
target="_blank"
rel="noreferrer"
passHref
legacyBehavior
>
GitHub
<Anchor target="_blank">GitHub</Anchor>
</Link>
{" | "}
<Link
href="https://jtiala.github.io/themeless-ui"
target="_blank"
rel="noreferrer"
passHref
legacyBehavior
>
Storybook
<Anchor target="_blank">Storybook</Anchor>
</Link>
</Text>
</footer>
Expand Down
7 changes: 4 additions & 3 deletions apps/nextjs-example/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Heading, List, Text } from "@themeless-ui/react";
import { Anchor, Heading, List, Text } from "@themeless-ui/react";
import { allPosts } from "contentlayer/generated";
import { compareDesc } from "date-fns";
import Link from "next/link";
Expand All @@ -16,8 +16,9 @@ export default async function Home() {
<List>
{posts.map((post) => (
<List.Item key={post._id}>
{/* TODO: use <Anchor> */}
<Link href={post.url}>{post.title}</Link>
<Link href={post.url} passHref legacyBehavior>
<Anchor>{post.title}</Anchor>
</Link>
</List.Item>
))}
</List>
Expand Down
2 changes: 1 addition & 1 deletion apps/nextjs-example/posts/lorem-ipsum.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ date: 2023-01-01

### Ipsum in blandit

Integer hendrerit, ipsum in blandit rhoncus, orci sapien ullamcorper nibh, vitae fringilla lectus velit eget magna. Curabitur erat justo, dictum vitae bibendum at, lacinia non dolor. Vestibulum consequat hendrerit sem vel consequat. Duis pellentesque volutpat justo sed elementum. Curabitur euismod tempor mollis.
Integer hendrerit, [ipsum in blandit](https://exaple.com) rhoncus, orci sapien ullamcorper nibh, vitae fringilla lectus velit eget magna. Curabitur erat justo, dictum vitae bibendum at, lacinia non dolor. Vestibulum consequat hendrerit sem vel consequat. Duis pellentesque volutpat justo sed elementum. Curabitur euismod tempor mollis.

- Nullam efficitur quis urna eget pellentesque.
- Cras elementum ullamcorper odio, dapibus ultrices justo mattis ut.
Expand Down
7 changes: 6 additions & 1 deletion apps/nextjs-example/utils/mdx.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Anchor,
Blockquote,
Heading,
List,
Expand All @@ -9,7 +10,11 @@ import type { MDXComponents } from "mdx/types";

export const mdxComponents: MDXComponents = {
p: ({ children }) => <Paragraph>{children}</Paragraph>,
a: ({ children }) => <Text>{children}</Text>, // TODO: use <Anchor>
a: ({ href, target, children }) => (
<Anchor href={href} target={target}>
{children}
</Anchor>
),
em: ({ children }) => <Text type="em">{children}</Text>,
strong: ({ children }) => <Text type="strong">{children}</Text>,
blockquote: ({ children }) => <Blockquote>{children}</Blockquote>,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
],
"packageManager": "[email protected]",
"scripts": {
"dev": "turbo run dev --no-cache --continue",
"dev": "turbo run dev --no-cache --continue --concurrency=12",
"build": "turbo run build",
"preview": "turbo run preview",
"test": "turbo run test",
Expand Down
55 changes: 55 additions & 0 deletions packages/react/src/components/Anchor/Anchor.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
componentToJson,
describe,
expect,
it,
render,
renderer,
screen,
} from "../../../test";
import { Anchor } from "./Anchor";

const href = "https://example.com";
const children = "Example.com";

const defaultAnchor = (
<Anchor testId="anchor" href={href}>
{children}
</Anchor>
);

const externalAnchor = (
<Anchor testId="anchor" href={href} target="_blank">
{children}
</Anchor>
);

describe("Anchor", async () => {
it("should render default anchor", () => {
expect(componentToJson(renderer.create(defaultAnchor))).toMatchSnapshot();

render(defaultAnchor);

const anchor = screen.getByTestId("anchor");
const linkRelationships = anchor.getAttribute("rel");

expect(anchor).toBeInTheDocument();
expect(anchor).toHaveTextContent(children);
expect(linkRelationships).toBeFalsy();
});

it("should render external anchor", () => {
expect(componentToJson(renderer.create(externalAnchor))).toMatchSnapshot();

render(externalAnchor);

const anchor = screen.getByTestId("anchor");
const linkRelationships = (anchor.getAttribute("rel") || "").split(" ");

expect(anchor).toBeInTheDocument();
expect(anchor).toHaveTextContent(children);
expect(anchor).toHaveAttribute("target", "_blank");
expect(linkRelationships).toContain("noopener");
expect(linkRelationships).toContain("noreferrer");
});
});
115 changes: 115 additions & 0 deletions packages/react/src/components/Anchor/Anchor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { CommonComponentProps, cn } from "@themeless-ui/utils";
import { AnchorHTMLAttributes, ReactNode } from "react";

type HTMLAnchorProps = AnchorHTMLAttributes<HTMLAnchorElement>;

export type AnchorProps = {
/**
* The URL that the hyperlink points to.
*/
href?: HTMLAnchorProps["href"];

/**
* The relationship of the linked URL.
*
* If `rel` is not defined and the anchor has `target` as `_blank`, the following relationships will be added automatically:
* - `noopener` to prevent reverse tabnabbing
* - `noreferrer` to prevent passing referrer information to the target
*/
rel?: HTMLAnchorProps["rel"];

/**
* Where to display the linked URL.
*/
target?: AnchorHTMLAttributes<HTMLAnchorElement>["target"];

/**
* Causes the browser to treat the linked URL as a download.
*/
download?: AnchorHTMLAttributes<HTMLAnchorElement>["download"];

/**
* A function that is called when the anchor is clicked.
*/
onClick?: AnchorHTMLAttributes<HTMLAnchorElement>["onClick"];

/**
* A function that is called when the anchor is focused.
*/
onFocus?: AnchorHTMLAttributes<HTMLAnchorElement>["onFocus"];

/**
* A function that is called when the anchor hast lost focus.
*/
onBlur?: AnchorHTMLAttributes<HTMLAnchorElement>["onBlur"];

/**
* A function that is called when mouse enters the anchor.
*/
onMouseEnter?: AnchorHTMLAttributes<HTMLAnchorElement>["onMouseEnter"];

/**
* A function that is called when mouse leaves the anchor.
*/
onMouseLeave?: AnchorHTMLAttributes<HTMLAnchorElement>["onMouseLeave"];

/**
* A function that is called when one or more touch points are placed on the touch surface.
*/
onTouchStart?: AnchorHTMLAttributes<HTMLAnchorElement>["onTouchStart"];

/**
* A function that is called when one or more touch points are removed from the touch surface.
*/
onTouchEnd?: AnchorHTMLAttributes<HTMLAnchorElement>["onTouchEnd"];

/**
* Anchor content.
*/
children?: ReactNode;
} & CommonComponentProps;

const className = cn("anchor");

/**
* Display a hyperlink.
*/
export function Anchor({
href,
rel,
target,
download,
onClick,
onFocus,
onBlur,
onMouseEnter,
onMouseLeave,
onTouchStart,
onTouchEnd,
children,
testId,
}: AnchorProps) {
const openInNewTab = target === "_blank";
const linkRelationships =
rel || openInNewTab ? "noopener noreferrer" : undefined;

return (
<a
href={href}
rel={linkRelationships}
target={target}
download={download}
onClick={onClick}
onFocus={onFocus}
onBlur={onBlur}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onTouchStart={onTouchStart}
onTouchEnd={onTouchEnd}
className={className}
data-testid={testId}
>
{children}
</a>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Anchor > should render default anchor 1`] = `
<a
className="tui__anchor"
data-testid="anchor"
href="https://example.com"
>
Example.com
</a>
`;

exports[`Anchor > should render external anchor 1`] = `
<a
className="tui__anchor"
data-testid="anchor"
href="https://example.com"
rel="noopener noreferrer"
target="_blank"
>
Example.com
</a>
`;
2 changes: 2 additions & 0 deletions packages/react/src/components/Anchor/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Anchor } from "./Anchor";
export type { AnchorProps } from "./Anchor";
Loading

0 comments on commit bc52a47

Please sign in to comment.