This repository has been archived by the owner on Sep 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: External link icon + open in new tab (#71)
- Loading branch information
Showing
14 changed files
with
682 additions
and
379 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,17 +44,14 @@ | |
"@vanilla-extract/webpack-plugin": "^2.3.1", | ||
"gatsby": "^5.12.12", | ||
"gatsby-adapter-netlify": "^1.0.4", | ||
"gatsby-plugin-catch-links": "^5.12.0", | ||
"gatsby-plugin-feed": "^5.12.3", | ||
"gatsby-plugin-gatsby-cloud": "^5.12.2", | ||
"gatsby-plugin-image": "^3.12.3", | ||
"gatsby-plugin-manifest": "^5.12.3", | ||
"gatsby-plugin-mdx": "^5.12.3", | ||
"gatsby-plugin-perf-budgets": "^0.0.18", | ||
"gatsby-plugin-sharp": "^5.12.3", | ||
"gatsby-plugin-sitemap": "^6.12.3", | ||
"gatsby-plugin-vanilla-extract": "^4.0.1", | ||
"gatsby-plugin-webpack-bundle-analyser-v2": "^1.1.32", | ||
"gatsby-remark-images": "^7.12.3", | ||
"gatsby-source-filesystem": "^5.12.1", | ||
"gatsby-source-graphql": "^5.12.1", | ||
|
@@ -75,7 +72,7 @@ | |
}, | ||
"devDependencies": { | ||
"@netlify/edge-functions": "^2.2.0", | ||
"@playwright/test": "^1.40.1", | ||
"@playwright/test": "^1.41.2", | ||
"@testing-library/jest-dom": "^6.1.5", | ||
"@testing-library/react": "^14.1.2", | ||
"@types/lodash": "^4.14.202", | ||
|
@@ -84,6 +81,7 @@ | |
"@types/react-dom": "^18.2.17", | ||
"@typescript-eslint/eslint-plugin": "^6.13.2", | ||
"@typescript-eslint/parser": "^6.13.2", | ||
"@vanilla-extract/vite-plugin": "^4.0.4", | ||
"cross-env": "^7.0.3", | ||
"cspell": "^7.3.9", | ||
"eslint": "^8.55.0", | ||
|
@@ -96,11 +94,11 @@ | |
"eslint-plugin-react": "^7.33.2", | ||
"eslint-plugin-react-hooks": "^4.6.0", | ||
"husky": "^8.0.3", | ||
"jsdom": "^22.1.0", | ||
"jsdom": "^24.0.0", | ||
"lint-staged": "^14.0.1", | ||
"prettier": "^3.1.1", | ||
"typescript": "^5.3.3", | ||
"vitest": "^1.0.4" | ||
"vitest": "^1.3.0" | ||
}, | ||
"packageManager": "[email protected]" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/** | ||
* @vitest-environment jsdom | ||
*/ | ||
|
||
import * as React from "react" | ||
import { render, screen } from "@testing-library/react" | ||
import { MarkdownLink } from "../link" | ||
|
||
describe(`MarkdownLink`, () => { | ||
it(`should render internal link`, () => { | ||
render(<MarkdownLink href="/internal">Internal Link</MarkdownLink>) | ||
const link = screen.getByRole(`link`) | ||
expect(link).toHaveAttribute(`data-link-internal`) | ||
}) | ||
|
||
it(`should render external link`, () => { | ||
render(<MarkdownLink href="https://example.com">External Link</MarkdownLink>) | ||
const link = screen.getByRole(`link`) | ||
expect(link).toHaveAttribute(`data-link-external`) | ||
expect(link).toHaveAttribute(`target`, `_blank`) | ||
expect(link).toHaveAttribute(`rel`, `noopener noreferrer`) | ||
expect(link).toHaveTextContent(`(opens in a new tab)`) | ||
}) | ||
|
||
it(`should render hash link`, () => { | ||
render(<MarkdownLink href="#hash">Hash Link</MarkdownLink>) | ||
const link = screen.getByRole(`link`) | ||
expect(link).toHaveAttribute(`href`, `#hash`) | ||
expect(link).not.toHaveAttribute(`data-link-internal`) | ||
expect(link).not.toHaveAttribute(`data-link-external`) | ||
}) | ||
|
||
it(`should render mailto link`, () => { | ||
render(<MarkdownLink href="mailto:[email protected]">Mailto Link</MarkdownLink>) | ||
const link = screen.getByRole(`link`) | ||
expect(link).toHaveAttribute(`href`, `mailto:[email protected]`) | ||
expect(link).not.toHaveAttribute(`data-link-internal`) | ||
expect(link).not.toHaveAttribute(`data-link-external`) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import * as React from "react" | ||
import { Link as GatsbyLink } from "gatsby" | ||
import { isInternalUrl } from "../../utils/is-internal-url" | ||
import { VisuallyHidden } from "../a11y/visually-hidden" | ||
|
||
/** | ||
* Use Gatsby's link component for internal links. | ||
* Set target="_blank" for external links and add data attribute for CSS styling. | ||
*/ | ||
export const MarkdownLink = ({ href, children, ...rest }) => { | ||
// If URL is a hash link, use anchor tag | ||
if (href.startsWith(`#`)) { | ||
return ( | ||
<a href={href} {...rest}> | ||
{children} | ||
</a> | ||
) | ||
} | ||
|
||
// If internal, use Gatsby's link component | ||
if (isInternalUrl(href)) { | ||
return ( | ||
<GatsbyLink data-link-internal to={href} {...rest}> | ||
{children} | ||
</GatsbyLink> | ||
) | ||
} | ||
|
||
// If URL is a protocol like mailto or tel, use anchor tag | ||
if (!href.startsWith(`http`)) { | ||
return ( | ||
<a href={href} {...rest}> | ||
{children} | ||
</a> | ||
) | ||
} | ||
|
||
// At this point the link can only be external, style as such | ||
return ( | ||
<a data-link-external target="_blank" rel="noopener noreferrer" href={href} {...rest}> | ||
{children} | ||
<VisuallyHidden> (opens in a new tab)</VisuallyHidden> | ||
</a> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { isInternalUrl } from "../is-internal-url" | ||
|
||
describe(`isInternalUrl`, () => { | ||
it(`returns true for internal URLs`, () => { | ||
expect(isInternalUrl(`/`)).toBe(true) | ||
expect(isInternalUrl(`/about/`)).toBe(true) | ||
expect(isInternalUrl(`/about/#anchor`)).toBe(true) | ||
expect(isInternalUrl(`https://www.lekoarts.de`)).toBe(true) | ||
expect(isInternalUrl(`https://www.lekoarts.de/about/`)).toBe(true) | ||
expect(isInternalUrl(`https://www.lekoarts.de/about/#anchor`)).toBe(true) | ||
}) | ||
|
||
it(`returns false for external URLs`, () => { | ||
expect(isInternalUrl(`https://example.com`)).toBe(false) | ||
expect(isInternalUrl(`https://example.com/about`)).toBe(false) | ||
expect(isInternalUrl(`https://example.com/about#anchor`)).toBe(false) | ||
expect(isInternalUrl(`https://example.com/about/`)).toBe(false) | ||
expect(isInternalUrl(`https://example.com/about/#anchor`)).toBe(false) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { site } from "../constants/meta.mjs" | ||
|
||
const base = new URL(site.url) | ||
|
||
export function isInternalUrl(url: string): boolean { | ||
return new URL(url, base).hostname === base.hostname | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.