Skip to content

Commit

Permalink
Merge pull request #7 from dbudwin/ChildComponentProp
Browse files Browse the repository at this point in the history
Support passing child component
  • Loading branch information
dbudwin authored Dec 26, 2020
2 parents c76e783 + d9e9c71 commit c28af8d
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 52 deletions.
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>
);
}

0 comments on commit c28af8d

Please sign in to comment.