diff --git a/README.md b/README.md
index c546fe2..02c4e34 100644
--- a/README.md
+++ b/README.md
@@ -29,31 +29,27 @@ Using `react-secure-link` for outbound links prevents the new tab from having ac
1. Add `react-secure-link` to your project via `npm install react-secure-link`
2. Import the package: `import { SecureLink } from "react-secure-link";`
-3. Use the following for links you want to open in a new tab: ``
+3. Use the following for links you want to open in a new tab: `react-secure-link on NPM`
### API
-`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` 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. |
-| `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). |
+`SecureLink` can be used to make text, images, or other children components clickable. In addition, standard `a` element attributes can be pass in as props (i.e. `href`, `className`, `id`, `role`, `style`).
### Basic Usage Example
```tsx
-
+
```
### Advance Usage Example
```tsx
console.log("Clicked")}
>
react-secure-link on NPM
diff --git a/package-lock.json b/package-lock.json
index fd9c4c3..27f86ba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
"name": "react-secure-link",
- "version": "2.0.0",
+ "version": "3.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
- "version": "2.0.0",
+ "version": "3.0.0",
"license": "GPL-3.0-or-later",
"devDependencies": {
"@babel/core": "^7.12.9",
diff --git a/package.json b/package.json
index 08a3837..457b9c7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-secure-link",
- "version": "2.0.0",
+ "version": "3.0.0",
"description": "A TypeScript compatible React component to avoid security exploits when opening a link in a new tab.",
"keywords": [
"react",
diff --git a/src/components/__tests__/secure-link.test.tsx b/src/components/__tests__/secure-link.test.tsx
index 923858d..57841b8 100644
--- a/src/components/__tests__/secure-link.test.tsx
+++ b/src/components/__tests__/secure-link.test.tsx
@@ -1,10 +1,9 @@
import "@testing-library/jest-dom";
-import React, { Key } from "react";
-import { render, screen } from "@testing-library/react";
+import { fireEvent, render, screen } from "@testing-library/react";
+import React from "react";
import { SecureLink } from "../secure-link";
-import each from "jest-each";
import faker from "faker";
let url: string;
@@ -13,93 +12,120 @@ beforeAll(() => {
url = faker.internet.url();
});
-const uniqueKeyPropValues = [
- undefined,
- null,
- faker.random.number(),
- faker.random.word(),
-];
-
-function renderSecureLinkWithoutChildren(uniqueKey: Key): void {
- render();
+function renderSecureLinkWithoutChildren(): void {
+ render();
}
-function renderSecureLinkWithChildren(text: string, uniqueKey: Key): void {
- render({text});
+function renderSecureLinkWithChildren(text: string): void {
+ render({text});
}
function getLinkByRole(): HTMLAnchorElement {
return screen.getByRole("link") as HTMLAnchorElement;
}
-each(uniqueKeyPropValues).describe(`when given uniqueKey: %s`, (uniqueKey?) => {
- describe("when not given children", () => {
- it("renders link without crashing", () => {
- renderSecureLinkWithoutChildren(uniqueKey);
+describe("when not given children", () => {
+ it("renders link without crashing", () => {
+ renderSecureLinkWithoutChildren();
- expect(getLinkByRole()).toBeInTheDocument();
- });
+ expect(getLinkByRole()).toBeInTheDocument();
+ });
- it("has given text", () => {
- renderSecureLinkWithoutChildren(uniqueKey);
+ it("has given text", () => {
+ renderSecureLinkWithoutChildren();
- expect(getLinkByRole()).toHaveTextContent(url);
- });
+ expect(getLinkByRole()).toHaveTextContent(url);
+ });
- it("links to given URL", () => {
- renderSecureLinkWithoutChildren(uniqueKey);
+ it("links to given URL", () => {
+ renderSecureLinkWithoutChildren();
- expect(getLinkByRole()).toHaveAttribute("href", url);
- });
+ expect(getLinkByRole()).toHaveAttribute("href", url);
+ });
- it("has expected attributes to open link securely", () => {
- renderSecureLinkWithoutChildren(uniqueKey);
+ it("has expected attributes to open link securely", () => {
+ renderSecureLinkWithoutChildren();
- expect(getLinkByRole()).toHaveAttribute("rel", "noopener noreferrer");
- });
+ expect(getLinkByRole()).toHaveAttribute("rel", "noopener noreferrer");
+ });
- it("has expected attributes to open link in new tab", () => {
- renderSecureLinkWithoutChildren(uniqueKey);
+ it("has expected attributes to open link in new tab", () => {
+ renderSecureLinkWithoutChildren();
- expect(getLinkByRole()).toHaveAttribute("target", "_blank");
- });
+ expect(getLinkByRole()).toHaveAttribute("target", "_blank");
});
- describe("when given children", () => {
- let text: string;
+ it(`can use intrinsic "a" element attributes`, () => {
+ const className = faker.random.word();
+ const style = { color: "red" };
+ const role = faker.random.word();
+ const handleClick = jest.fn();
+
+ render();
- beforeAll(() => {
- text = faker.lorem.word();
- });
+ const link = screen.getByRole(role);
- it("renders link without crashing", () => {
- renderSecureLinkWithChildren(text, uniqueKey);
+ fireEvent.click(link);
- expect(getLinkByRole()).toBeInTheDocument();
- });
+ expect(link).toBeInTheDocument();
+ expect(link).toHaveClass(className, { exact: true });
+ expect(link).toHaveStyle(style);
+ expect(handleClick).toHaveBeenCalledTimes(1);
+ });
+});
+
+describe("when given children", () => {
+ let text: string;
- it("has given text", () => {
- renderSecureLinkWithChildren(text, uniqueKey);
+ beforeAll(() => {
+ text = faker.lorem.word();
+ });
+
+ it("renders link without crashing", () => {
+ renderSecureLinkWithChildren(text);
- expect(getLinkByRole()).toHaveTextContent(text);
- });
+ expect(getLinkByRole()).toBeInTheDocument();
+ });
- it("links to given URL", () => {
- renderSecureLinkWithChildren(text, uniqueKey);
+ it("has given text", () => {
+ renderSecureLinkWithChildren(text);
+
+ expect(getLinkByRole()).toHaveTextContent(text);
+ });
+
+ it("links to given URL", () => {
+ renderSecureLinkWithChildren(text);
+
+ expect(getLinkByRole()).toHaveAttribute("href", url);
+ });
+
+ it("has expected attributes to open link securely", () => {
+ renderSecureLinkWithChildren(text);
+
+ expect(getLinkByRole()).toHaveAttribute("rel", "noopener noreferrer");
+ });
+
+ it("has expected attributes to open link in new tab", () => {
+ renderSecureLinkWithChildren(text);
+
+ expect(getLinkByRole()).toHaveAttribute("target", "_blank");
+ });
- expect(getLinkByRole()).toHaveAttribute("href", url);
- });
+ it(`can use intrinsic "a" element attributes`, () => {
+ const className = faker.random.word();
+ const style = { color: "red" };
+ const role = faker.random.word();
+ const handleClick = jest.fn();
- it("has expected attributes to open link securely", () => {
- renderSecureLinkWithChildren(text, uniqueKey);
+ render();
- expect(getLinkByRole()).toHaveAttribute("rel", "noopener noreferrer");
- });
+ const link = screen.getByRole(role);
- it("has expected attributes to open link in new tab", () => {
- renderSecureLinkWithChildren(text, uniqueKey);
+ fireEvent.click(link);
- expect(getLinkByRole()).toHaveAttribute("target", "_blank");
- });
+ expect(link).toBeInTheDocument();
+ expect(link).toHaveClass(className, { exact: true });
+ expect(link).toHaveStyle(style);
+ expect(handleClick).toHaveBeenCalledTimes(1);
});
});
diff --git a/src/components/secure-link.tsx b/src/components/secure-link.tsx
index efc8080..ce82146 100644
--- a/src/components/secure-link.tsx
+++ b/src/components/secure-link.tsx
@@ -1,19 +1,15 @@
-import React, { Key, ReactElement } from "react";
+import React, { ReactElement } from "react";
-interface SecureLinkProps extends React.HTMLAttributes {
- url: string;
- uniqueKey?: Key;
-}
+type SecureLinkProps = JSX.IntrinsicElements["a"]
-export function SecureLink({ url, uniqueKey, children }: SecureLinkProps): ReactElement {
+export function SecureLink({ ...props }: SecureLinkProps): ReactElement {
return (
- {children ? children : url}
+ {props.children ? props.children : props.href}
);
}