Skip to content

Commit 507da36

Browse files
authored
Migrate the TabNav component to use CSS modules (#6176)
1 parent ac5a9c1 commit 507da36

File tree

5 files changed

+86
-75
lines changed

5 files changed

+86
-75
lines changed

.changeset/thin-bears-change.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": minor
3+
---
4+
5+
Migrate the TabNav component to use CSS modules
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.TabNavTabList {
2+
display: flex;
3+
/* stylelint-disable-next-line primer/spacing */
4+
margin-bottom: -1px;
5+
overflow: auto;
6+
}
7+
8+
.TabNavNav {
9+
margin-top: 0;
10+
border-bottom: var(--borderWidth-thin) solid var(--borderColor-default);
11+
}
12+
13+
.TabNavLink {
14+
padding: var(--base-size-8) var(--base-size-12);
15+
font-size: var(--text-body-size-medium);
16+
/* stylelint-disable-next-line primer/typography */
17+
line-height: 20px;
18+
color: var(--fgColor-default);
19+
text-decoration: none;
20+
background-color: transparent;
21+
border: var(--borderWidth-thin) solid transparent;
22+
border-bottom: 0;
23+
24+
&:hover,
25+
&:focus {
26+
color: var(--fgColor-default);
27+
text-decoration: none;
28+
29+
@mixin focusOutline -6px;
30+
}
31+
32+
&.Selected {
33+
color: var(--fgColor-default);
34+
background-color: var(--bgColor-default);
35+
border-color: var(--borderColor-default);
36+
border-top-left-radius: var(--borderRadius-medium);
37+
border-top-right-radius: var(--borderRadius-medium);
38+
}
39+
}

packages/react/src/TabNav/TabNav.tsx

Lines changed: 29 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,17 @@
1-
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
21
import {clsx} from 'clsx'
32
import type {To} from 'history'
43
import React, {useRef, useState} from 'react'
5-
import styled from 'styled-components'
6-
import {get} from '../constants'
74
import {FocusKeys, useFocusZone} from '../hooks/useFocusZone'
85
import type {SxProp} from '../sx'
9-
import sx from '../sx'
106
import type {ComponentProps} from '../utils/types'
11-
import getGlobalFocusStyles from '../internal/utils/getGlobalFocusStyles'
127

13-
const ITEM_CLASS = 'TabNav-item'
14-
const SELECTED_CLASS = 'selected'
15-
16-
const TabNavBase = styled.div<SxProp>`
17-
${sx}
18-
`
19-
20-
const TabNavTabList = styled.div`
21-
display: flex;
22-
margin-bottom: -1px;
23-
overflow: auto;
24-
`
25-
26-
const TabNavNav = styled.nav`
27-
margin-top: 0;
28-
border-bottom: 1px solid ${get('colors.border.default')};
29-
`
8+
import styles from './TabNav.module.css'
9+
import {BoxWithFallback} from '../internal/components/BoxWithFallback'
3010

3111
/**
3212
* @deprecated
3313
*/
34-
export type TabNavProps = ComponentProps<typeof TabNavBase>
14+
export type TabNavProps = ComponentProps<typeof BoxWithFallback>
3515

3616
/**
3717
* @deprecated
@@ -73,11 +53,13 @@ function TabNav({children, 'aria-label': ariaLabel, ...rest}: TabNavProps) {
7353
)
7454

7555
return (
76-
<TabNavBase {...rest} ref={navRef as React.RefObject<HTMLDivElement>}>
77-
<TabNavNav aria-label={ariaLabel}>
78-
<TabNavTabList role="tablist">{children}</TabNavTabList>
79-
</TabNavNav>
80-
</TabNavBase>
56+
<BoxWithFallback {...rest} ref={navRef as React.RefObject<HTMLDivElement>}>
57+
<nav aria-label={ariaLabel} className={styles.TabNavNav}>
58+
<div role="tablist" className={styles.TabNavTabList}>
59+
{children}
60+
</div>
61+
</nav>
62+
</BoxWithFallback>
8163
)
8264
}
8365

@@ -88,44 +70,30 @@ export type TabNavLinkProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLA
8870
to?: To
8971
selected?: boolean
9072
href?: string
73+
className?: string
74+
as?: React.ElementType | 'a' | 'button' | 'div'
75+
disabled?: boolean
9176
} & SxProp
9277

9378
/**
9479
* @deprecated
9580
*/
96-
const TabNavLink = styled.a.attrs<TabNavLinkProps>(props => ({
97-
className: clsx(ITEM_CLASS, props.selected && SELECTED_CLASS, props.className),
98-
role: 'tab',
99-
'aria-selected': !!props.selected,
100-
tabIndex: -1,
101-
}))<TabNavLinkProps>`
102-
padding: 8px 12px;
103-
font-size: ${get('fontSizes.1')};
104-
line-height: 20px;
105-
color: ${get('colors.fg.default')};
106-
text-decoration: none;
107-
background-color: transparent;
108-
border: 1px solid transparent;
109-
border-bottom: 0;
110-
111-
${getGlobalFocusStyles('-6px')};
112-
113-
&:hover,
114-
&:focus {
115-
color: ${get('colors.fg.default')};
116-
text-decoration: none;
117-
}
118-
119-
&.selected {
120-
color: ${get('colors.fg.default')};
121-
border-color: ${get('colors.border.default')};
122-
border-top-right-radius: ${get('radii.2')};
123-
border-top-left-radius: ${get('radii.2')};
124-
background-color: ${get('colors.canvas.default')};
125-
}
126-
127-
${sx};
128-
` as PolymorphicForwardRefComponent<'a', TabNavLinkProps>
81+
const TabNavLink = React.forwardRef<HTMLAnchorElement, TabNavLinkProps>(function TabNavLink(
82+
{selected, className, as = 'a', ...rest}: TabNavLinkProps,
83+
ref,
84+
) {
85+
return (
86+
<BoxWithFallback
87+
as={as}
88+
ref={ref}
89+
role="tab"
90+
tabIndex={-1}
91+
aria-selected={selected ? true : undefined}
92+
className={clsx('TabNav-item', styles.TabNavLink, selected && 'selected', selected && styles.Selected, className)}
93+
{...rest}
94+
/>
95+
)
96+
})
12997

13098
TabNavLink.displayName = 'TabNav.Link'
13199

packages/react/src/TabNav/__tests__/TabNav.test.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ import TabNav from '..'
33
import {render, screen} from '@testing-library/react'
44
import userEvent from '@testing-library/user-event'
55
import {Button} from '../../Button'
6-
import Box from '../../Box'
76

87
describe('TabNav', () => {
98
const tabNavMarkup = (
10-
<Box>
9+
<div>
1110
<TabNav>
1211
<TabNav.Link id="first" href="#" as="div">
1312
First
@@ -21,7 +20,7 @@ describe('TabNav', () => {
2120
</TabNav.Link>
2221
</TabNav>
2322
<Button id="my-button">My Button</Button>
24-
</Box>
23+
</div>
2524
)
2625

2726
describe('TabNav.Link', () => {

packages/react/src/TabNav/__tests__/TabNav.types.test.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@ export function shouldAcceptCallWithNoProps() {
1010
)
1111
}
1212

13-
export function shouldNotAcceptSystemProps() {
14-
return (
15-
<>
16-
{/* @ts-expect-error system props should not be accepted */}
17-
<TabNav backgroundColor="maroon" />
18-
{/* @ts-expect-error system props should not be accepted */}
19-
<TabNav.Link backgroundColor="fuchsia" />
20-
</>
21-
)
22-
}
23-
2413
export function shouldAcceptButtonAsProps() {
2514
return <TabNav.Link as={Button} />
2615
}
2716

2817
export function shouldAcceptTabNavLinkprops() {
2918
return <TabNav.Link to="to something" selected as={Button} />
3019
}
20+
21+
export function shouldAcceptDisableProps() {
22+
return (
23+
<TabNav.Link
24+
disabled={true}
25+
onClick={() => {
26+
// noop
27+
}}
28+
/>
29+
)
30+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy