Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ Using `react-secure-link` for outbound links prevents the new tab from having ac

### API

In addition to any prop defined as part of the `React.HTMLAttributes<HTMLAnchorElement>` interface (i.e. `className`, `id`, `role`, `style`), the `SecureLink` component has the following custom props:
`SecureLink` can be used to make text, images, or other children components clickable. In addition to any prop defined as part of the `React.HTMLAttributes<HTMLAnchorElement>` interface (i.e. `className`, `id`, `role`, `style`), the `SecureLink` component has the following custom props:

| prop | Required | Type | Description |
|-------------|----------|----------|--------------------------------------------------------------------------|
| `url` | Yes | `string` | The URL to navigate to. |
| `text` | No | `string` | The text to show. If not provided, the given URL will be shown instead. |
| `uniqueKey` | No | `string` or `number` | A unique key to identify the link. This is being used as a `key` value for the component. For more information, refer to [React's website about keys](https://reactjs.org/docs/lists-and-keys.html#keys). |

### Basic Usage Example
Expand All @@ -52,9 +51,10 @@ In addition to any prop defined as part of the `React.HTMLAttributes<HTMLAnchorE
```tsx
<SecureLink
url="https://www.npmjs.com/package/react-secure-link"
text="react-secure-link on NPM"
className="no-link-decoration"
style={{ color: "red" }}
uniqueKey={123}
/>
>
react-secure-link on NPM
</SecureLink>
```
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-secure-link",
"version": "1.1.0",
"version": "2.0.0",
"description": "A TypeScript compatible React component to avoid security exploits when opening a link in a new tab.",
"keywords": [
"react",
Expand Down
92 changes: 52 additions & 40 deletions src/components/__tests__/secure-link.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,81 +13,93 @@ beforeAll(() => {
url = faker.internet.url();
});

const emptyTextValues = [
undefined,
null,
"",
];

const uniqueKeyPropValues = [
undefined,
null,
faker.random.number(),
faker.random.word(),
];

function renderSecureLink(text: string, uniqueKey: Key): void {
render(<SecureLink text={text} url={url} key={uniqueKey} />);
function renderSecureLinkWithoutChildren(uniqueKey: Key): void {
render(<SecureLink url={url} key={uniqueKey} />);
}

function renderSecureLinkWithChildren(text: string, uniqueKey: Key): void {
render(<SecureLink url={url} key={uniqueKey}>{text}</SecureLink>);
}

function getLinkByRole(): HTMLAnchorElement {
return screen.getByRole("link") as HTMLAnchorElement;
}

function itRendersWithoutCrashing(text: string, uniqueKey: Key): void {
it("renders link without crashing", () => {
renderSecureLink(text, uniqueKey);
each(uniqueKeyPropValues).describe(`when given uniqueKey: %s`, (uniqueKey?) => {
describe("when not given children", () => {
it("renders link without crashing", () => {
renderSecureLinkWithoutChildren(uniqueKey);

expect(getLinkByRole()).toBeInTheDocument();
});
}
expect(getLinkByRole()).toBeInTheDocument();
});

function itHasExpectedAttributes(text: string, uniqueKey: Key): void {
it("links to given URL", () => {
renderSecureLink(text, uniqueKey);
it("has given text", () => {
renderSecureLinkWithoutChildren(uniqueKey);

expect(getLinkByRole()).toHaveAttribute("href", url);
});
expect(getLinkByRole()).toHaveTextContent(url);
});

it("has expected attributes to open link securely", () => {
renderSecureLink(text, uniqueKey);
it("links to given URL", () => {
renderSecureLinkWithoutChildren(uniqueKey);

expect(getLinkByRole()).toHaveAttribute("rel", "noopener noreferrer");
});
expect(getLinkByRole()).toHaveAttribute("href", url);
});

it("has expected attributes to open link in new tab", () => {
renderSecureLink(text, uniqueKey);
it("has expected attributes to open link securely", () => {
renderSecureLinkWithoutChildren(uniqueKey);

expect(getLinkByRole()).toHaveAttribute("target", "_blank");
});
}

each(uniqueKeyPropValues).describe(`when given uniqueKey="%s"`, (uniqueKey?) => {
each(emptyTextValues).describe(`when given empty text="%s"`, (text?) => {
itRendersWithoutCrashing(text, uniqueKey);
itHasExpectedAttributes(text, uniqueKey);
expect(getLinkByRole()).toHaveAttribute("rel", "noopener noreferrer");
});

it("text is the same as the URL", () => {
renderSecureLink(text, uniqueKey);
it("has expected attributes to open link in new tab", () => {
renderSecureLinkWithoutChildren(uniqueKey);

expect(getLinkByRole()).toHaveTextContent(url);
expect(getLinkByRole()).toHaveAttribute("target", "_blank");
});
});

describe("when given text", () => {
describe("when given children", () => {
let text: string;

beforeAll(() => {
text = faker.lorem.word();
});

itRendersWithoutCrashing(text, uniqueKey);
itHasExpectedAttributes(text, uniqueKey);
it("renders link without crashing", () => {
renderSecureLinkWithChildren(text, uniqueKey);

expect(getLinkByRole()).toBeInTheDocument();
});

it("has given text", () => {
renderSecureLink(text, uniqueKey);
renderSecureLinkWithChildren(text, uniqueKey);

expect(getLinkByRole()).toHaveTextContent(text);
});

it("links to given URL", () => {
renderSecureLinkWithChildren(text, uniqueKey);

expect(getLinkByRole()).toHaveAttribute("href", url);
});

it("has expected attributes to open link securely", () => {
renderSecureLinkWithChildren(text, uniqueKey);

expect(getLinkByRole()).toHaveAttribute("rel", "noopener noreferrer");
});

it("has expected attributes to open link in new tab", () => {
renderSecureLinkWithChildren(text, uniqueKey);

expect(getLinkByRole()).toHaveAttribute("target", "_blank");
});
});
});
7 changes: 2 additions & 5 deletions src/components/secure-link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@ import React, { Key, ReactElement } from "react";

interface SecureLinkProps extends React.HTMLAttributes<HTMLAnchorElement> {
url: string;
text?: string;
uniqueKey?: Key;
}

export function SecureLink({ url, text, uniqueKey }: SecureLinkProps): ReactElement {
export function SecureLink({ url, uniqueKey, children }: SecureLinkProps): ReactElement {
return (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
key={uniqueKey}
>
{
text ? text : url
}
{children ? children : url}
</a>
);
}