Skip to content

Commit 723bf84

Browse files
authored
Merge pull request #1014 from primer/cb/ts-underline-nav
Migrate UnderlineNav to TypeScript
2 parents 3033b57 + df2920f commit 723bf84

File tree

8 files changed

+94
-26
lines changed

8 files changed

+94
-26
lines changed

.changeset/wet-flowers-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/components": patch
3+
---
4+
5+
Migrate `UnderlineNav` to TypeScript

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"@styled-system/prop-types": "5.1.2",
4747
"@styled-system/props": "5.1.4",
4848
"@styled-system/theme-get": "5.1.2",
49+
"@types/history": "4.7.8",
4950
"@types/styled-components": "5.1.7",
5051
"@types/styled-system": "5.1.10",
5152
"@types/styled-system__css": "5.0.14",

src/UnderlineNav.js renamed to src/UnderlineNav.tsx

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
1-
import React from 'react'
2-
import PropTypes from 'prop-types'
31
import classnames from 'classnames'
2+
import PropTypes from 'prop-types'
3+
import React from 'react'
44
import styled from 'styled-components'
5-
import {COMMON, get} from './constants'
5+
import {COMMON, get, SystemCommonProps} from './constants'
6+
import sx, {SxProp} from './sx'
67
import theme from './theme'
7-
import sx from './sx'
8+
import {ComponentProps} from './utils/types'
9+
import * as History from 'history'
810

911
const ITEM_CLASS = 'UnderlineNav-item'
1012
const SELECTED_CLASS = 'selected'
1113

12-
function UnderlineNavBase({actions, className, align, children, full, label, theme, ...rest}) {
13-
const classes = classnames(className, 'UnderlineNav', align && `UnderlineNav--${align}`, full && 'UnderlineNav--full')
14-
return (
15-
<nav className={classes} aria-label={label} {...rest}>
16-
<div className="UnderlineNav-body">{children}</div>
17-
{actions && <div className="UnderlineNav-actions">{actions}</div>}
18-
</nav>
19-
)
20-
}
21-
22-
const UnderlineNav = styled(UnderlineNavBase)`
14+
const UnderlineNavBase = styled.nav`
2315
display: flex;
2416
justify-content: space-between;
2517
border-bottom: 1px solid #eaecef;
@@ -52,10 +44,33 @@ const UnderlineNav = styled(UnderlineNavBase)`
5244
${sx};
5345
`
5446

55-
UnderlineNav.Link = styled.a.attrs(props => ({
47+
export type UnderlineNavProps = {
48+
actions?: React.ReactNode
49+
align?: 'right'
50+
full?: boolean
51+
label?: string
52+
} & ComponentProps<typeof UnderlineNavBase>
53+
54+
function UnderlineNav({actions, className, align, children, full, label, theme, ...rest}: UnderlineNavProps) {
55+
const classes = classnames(className, 'UnderlineNav', align && `UnderlineNav--${align}`, full && 'UnderlineNav--full')
56+
return (
57+
<UnderlineNavBase className={classes} aria-label={label} theme={theme} {...rest}>
58+
<div className="UnderlineNav-body">{children}</div>
59+
{actions && <div className="UnderlineNav-actions">{actions}</div>}
60+
</UnderlineNavBase>
61+
)
62+
}
63+
64+
type StyledUnderlineNavLinkProps = {
65+
to?: History.LocationDescriptor
66+
selected?: boolean
67+
} & SystemCommonProps &
68+
SxProp
69+
70+
const UnderlineNavLink = styled.a.attrs<StyledUnderlineNavLinkProps>(props => ({
5671
activeClassName: typeof props.to === 'string' ? 'selected' : '',
5772
className: classnames(ITEM_CLASS, props.selected && SELECTED_CLASS, props.className)
58-
}))`
73+
}))<StyledUnderlineNavLinkProps>`
5974
padding: ${get('space.3')} ${get('space.2')};
6075
margin-right: ${get('space.3')};
6176
font-size: ${get('fontSizes.1')};
@@ -105,18 +120,18 @@ UnderlineNav.propTypes = {
105120
...sx.propTypes
106121
}
107122

108-
UnderlineNav.Link.defaultProps = {
123+
UnderlineNavLink.defaultProps = {
109124
theme
110125
}
111126

112-
UnderlineNav.Link.propTypes = {
113-
as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
127+
UnderlineNavLink.propTypes = {
114128
href: PropTypes.string,
115129
selected: PropTypes.bool,
116130
...COMMON.propTypes,
117131
...sx.propTypes
118132
}
119133

120-
UnderlineNav.Link.displayName = 'UnderlineNav.Link'
134+
UnderlineNavLink.displayName = 'UnderlineNav.Link'
121135

122-
export default UnderlineNav
136+
export type UnderlineNavLinkProps = ComponentProps<typeof UnderlineNavLink>
137+
export default Object.assign(UnderlineNav, {Link: UnderlineNavLink})
File renamed without changes.

src/__tests__/UnderlineNavLink.js renamed to src/__tests__/UnderlineNavLink.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ describe('UnderlineNav.Link', () => {
2626
})
2727

2828
it('adds activeClassName={SELECTED_CLASS} when it gets a "to" prop', () => {
29-
const Mock = jest.fn(() => <div />)
30-
render(<UnderlineNav.Link as={Mock} to="#" />)
31-
expect(Mock.mock.calls[0][0].to).toEqual('#')
32-
expect(Mock.mock.calls[0][0].activeClassName).toEqual('selected')
29+
const Link = ({theme, ...props}: any) => <div {...props} />
30+
expect(render(<UnderlineNav.Link as={Link} to="#" />)).toMatchSnapshot()
3331
})
3432
})

src/__tests__/__snapshots__/UnderlineNavLink.js.snap renamed to src/__tests__/__snapshots__/UnderlineNavLink.tsx.snap

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,49 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`UnderlineNav.Link adds activeClassName={SELECTED_CLASS} when it gets a "to" prop 1`] = `
4+
.c0 {
5+
padding: 16px 8px;
6+
margin-right: 16px;
7+
font-size: 14px;
8+
line-height: 1.5;
9+
color: #586069;
10+
text-align: center;
11+
border-bottom: 2px solid transparent;
12+
-webkit-text-decoration: none;
13+
text-decoration: none;
14+
}
15+
16+
.c0:hover,
17+
.c0:focus {
18+
color: #24292e;
19+
-webkit-text-decoration: none;
20+
text-decoration: none;
21+
border-bottom-color: #e1e4e8;
22+
-webkit-transition: 0.2s ease;
23+
transition: 0.2s ease;
24+
}
25+
26+
.c0:hover .UnderlineNav-octicon,
27+
.c0:focus .UnderlineNav-octicon {
28+
color: #6a737d;
29+
}
30+
31+
.c0.selected {
32+
color: #24292e;
33+
border-bottom-color: #f66a0a;
34+
}
35+
36+
.c0.selected .UnderlineNav-octicon {
37+
color: #6a737d;
38+
}
39+
40+
<div
41+
activeClassName="selected"
42+
className="c0 UnderlineNav-item"
43+
to="#"
44+
/>
45+
`;
46+
347
exports[`UnderlineNav.Link renders consistently 1`] = `
448
.c0 {
549
padding: 16px 8px;

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1820,6 +1820,11 @@
18201820
dependencies:
18211821
"@types/node" "*"
18221822

1823+
"@types/history@4.7.8":
1824+
version "4.7.8"
1825+
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934"
1826+
integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==
1827+
18231828
"@types/hoist-non-react-statics@*":
18241829
version "3.3.1"
18251830
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"

0 commit comments

Comments
 (0)