Skip to content

Commit

Permalink
Merge pull request #488 from primer/link
Browse files Browse the repository at this point in the history
Allow Link to render any valid component type
  • Loading branch information
Emily authored Jul 19, 2019
2 parents bc8d843 + 82f62a2 commit a1b9852
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 70 deletions.
121 changes: 60 additions & 61 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"react": "^16.8.0",
"react-bodymovin": "2.0.0",
"react-dom": "^16.8.1",
"react-is": "16.8.6",
"styled-system": "5.0.5"
},
"devDependencies": {
Expand Down
12 changes: 7 additions & 5 deletions pages/components/docs/Link.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ The Link component styles anchor tags with default blue styling and hover text d

In special cases where you'd like a `<button>` styled like a `Link`, use `<Link as='button'>`. Make sure to provide a click handler with `onClick`.

**Important:** When using the `as` prop, be sure to always render an accessible element type, like `a`, `button`, `input`, or `summary`.

## Default example

```.jsx
Expand All @@ -16,10 +18,10 @@ Link components get `COMMON` and `TYPOGRAPHY` system props. Read our [System Pro

## Component props

| Name | Type | Default | Description |
| :- | :- | :-: | :- |
| href | String | | URL to be used for the Link |
| underline | Boolean | false | Adds underline to the Link |
| as | String | 'a' | Can be 'a', 'button', 'input', or 'summary'
| Name | Type | Default | Description |
| :-------- | :------ | :-----: | :------------------------------------------ |
| href | String | | URL to be used for the Link |
| underline | Boolean | false | Adds underline to the Link |
| as | String | 'a' | Can be 'a', 'button', 'input', or 'summary' |

export const meta = {displayName: 'Link'}
9 changes: 5 additions & 4 deletions src/Link.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import PropTypes from 'prop-types'
import styled from 'styled-components'
import {TYPOGRAPHY, COMMON} from './constants'
import theme from './theme'
import {system} from 'styled-system'
import {COMMON, TYPOGRAPHY} from './constants'
import theme from './theme'
import elementType from './utils/elementType'

const buttonStyles = {
display: 'inline-block',
Expand All @@ -29,7 +30,7 @@ const Link = styled.a`
text-decoration: underline;
${hoverColor};
}
${props => (props.is === 'button' ? buttonStyles : '')};
${props => (props.as === 'button' ? buttonStyles : '')};
${TYPOGRAPHY} ${COMMON};
`

Expand All @@ -39,7 +40,7 @@ Link.defaultProps = {
}

Link.propTypes = {
as: PropTypes.oneOf(['a', 'button', 'input', 'summary']),
as: elementType,
href: PropTypes.string,
theme: PropTypes.object,
underline: PropTypes.bool,
Expand Down
12 changes: 12 additions & 0 deletions src/__tests__/Link.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ describe('Link', () => {
expect(render(<Link as="button" />).type).toEqual('button')
})

it('respects the "as" prop with non-string component types', () => {
function ExampleLink({children}) {
return <a className="example-link">{children}</a>
}

expect(render(<Link as={ExampleLink}>Example</Link>)).toMatchSnapshot()
})

it('respects hoverColor prop', () => {
expect(render(<Link hoverColor="blue.4" />)).toMatchSnapshot()
})
Expand All @@ -34,4 +42,8 @@ describe('Link', () => {
expect(render(<Link fontStyle="italic" />)).toHaveStyleRule('font-style', 'italic')
expect(render(<Link as="i" fontStyle="normal" />)).toHaveStyleRule('font-style', 'normal')
})

it('applies button styles when rendering a button element', () => {
expect(render(<Link as="button" />)).toMatchSnapshot()
})
})
40 changes: 40 additions & 0 deletions src/__tests__/__snapshots__/Link.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Link applies button styles when rendering a button element 1`] = `
.c0 {
-webkit-text-decoration: none;
text-decoration: none;
display: inline-block;
padding: 0;
font-size: inherit;
white-space: nowrap;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: transparent;
border: 0;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
color: #0366d6;
}
.c0:hover {
-webkit-text-decoration: underline;
text-decoration: underline;
}
<button
className="c0"
color="blue.5"
/>
`;

exports[`Link passes href down to link element 1`] = `
.c0 {
-webkit-text-decoration: none;
Expand Down Expand Up @@ -55,3 +87,11 @@ exports[`Link respects hoverColor prop 1`] = `
color="blue.5"
/>
`;

exports[`Link respects the "as" prop with non-string component types 1`] = `
<a
className="example-link"
>
Example
</a>
`;
14 changes: 14 additions & 0 deletions src/utils/elementType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {isValidElementType} from 'react-is'

// This function is a temporary workaround until we can get
// the official PropTypes.elementType working (https://git.io/fjMLX).
// PropTypes.elementType is currently `undefined` in the browser.
function elementType(props, propName, componentName) {
if (props[propName] && !isValidElementType(props[propName])) {
return new Error(
`Invalid prop '${propName}' supplied to '${componentName}': the prop is not a valid React component`
)
}
}

export default elementType

0 comments on commit a1b9852

Please sign in to comment.