Skip to content

Commit 37a91b5

Browse files
authoredFeb 14, 2025··
chore(Header): Remove CSS modules feature flag from Header (#5510)
* Remove CSS modules feature flag from Header * Adjust Header for as prop * Create shaggy-comics-whisper.md * Remove FeatureFlagElement from Header tests * Remove unused FeatureFlags import in Header test
1 parent 56baa0a commit 37a91b5

File tree

3 files changed

+52
-145
lines changed

3 files changed

+52
-145
lines changed
 

‎.changeset/shaggy-comics-whisper.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": minor
3+
---
4+
5+
Remove CSS modules feature flag from Header

‎packages/react/src/Header/Header.tsx

+46-104
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,81 @@
11
import type {Location, Pathname} from 'history'
2-
import styled, {css} from 'styled-components'
3-
import {get} from '../constants'
42
import type {SxProp} from '../sx'
5-
import sx from '../sx'
6-
import type {ComponentProps} from '../utils/types'
7-
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
8-
import {useFeatureFlag} from '../FeatureFlags'
93
import React from 'react'
104
import {clsx} from 'clsx'
115
import classes from './Header.module.css'
126
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
7+
import {defaultSxProp} from '../utils/defaultSxProp'
8+
import Box from '../Box'
139

14-
type StyledHeaderProps = React.ComponentProps<'header'> & SxProp
15-
type StyledHeaderItemProps = React.ComponentProps<'div'> & SxProp & {full?: boolean}
16-
type StyledHeaderLinkProps = React.ComponentProps<'a'> & SxProp & {to?: Location | Pathname}
10+
export type HeaderProps = React.ComponentProps<'header'> & SxProp & {as?: React.ElementType}
11+
export type HeaderItemProps = React.ComponentProps<'div'> & SxProp & {full?: boolean}
12+
export type HeaderLinkProps = React.ComponentProps<'a'> & SxProp & {to?: Location | Pathname; as?: React.ElementType}
1713

18-
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_ga'
19-
20-
const StyledHeader = toggleStyledComponent(
21-
CSS_MODULES_FEATURE_FLAG,
22-
'header',
23-
styled.header<StyledHeaderProps>`
24-
z-index: 32;
25-
display: flex;
26-
padding: ${get('space.3')};
27-
font-size: ${get('fontSizes.1')};
28-
line-height: ${get('lineHeights.default')};
29-
color: ${get('colors.header.text')};
30-
background-color: ${get('colors.header.bg')};
31-
align-items: center;
32-
flex-wrap: nowrap;
33-
overflow: auto;
34-
35-
${sx};
36-
`,
37-
)
38-
39-
const Header = React.forwardRef<HTMLElement, StyledHeaderProps>(function Header(
40-
{children, className, ...rest},
14+
const Header = React.forwardRef<HTMLElement, HeaderProps>(function Header(
15+
{children, className, sx: sxProp = defaultSxProp, as = 'header', ...rest},
4116
forwardRef,
4217
) {
43-
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
18+
if (sxProp !== defaultSxProp || as !== 'header') {
19+
return (
20+
<Box as={as} sx={sxProp} ref={forwardRef} className={clsx(className, classes.Header)} {...rest}>
21+
{children}
22+
</Box>
23+
)
24+
}
4425
return (
45-
<StyledHeader ref={forwardRef} className={clsx(className, {[classes.Header]: enabled})} {...rest}>
26+
<header ref={forwardRef} className={clsx(className, classes.Header)} {...rest}>
4627
{children}
47-
</StyledHeader>
28+
</header>
4829
)
49-
}) as PolymorphicForwardRefComponent<'header', StyledHeaderProps>
30+
}) as PolymorphicForwardRefComponent<'header', HeaderProps>
5031

5132
Header.displayName = 'Header'
5233

53-
const StyledHeaderItem = toggleStyledComponent(
54-
CSS_MODULES_FEATURE_FLAG,
55-
'div',
56-
styled.div<StyledHeaderItemProps>`
57-
display: flex;
58-
margin-right: ${get('space.3')};
59-
align-self: stretch;
60-
align-items: center;
61-
flex-wrap: nowrap;
62-
63-
${({full}) =>
64-
full &&
65-
css`
66-
flex: auto;
67-
`};
68-
69-
${sx};
70-
`,
71-
)
72-
73-
const HeaderItem = React.forwardRef<HTMLElement, StyledHeaderItemProps>(function HeaderItem(
74-
{children, className, ...rest},
34+
const HeaderItem = React.forwardRef<HTMLDivElement, HeaderItemProps>(function HeaderItem(
35+
{children, className, sx: sxProp = defaultSxProp, full, ...rest},
7536
forwardRef,
7637
) {
77-
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
38+
if (sxProp !== defaultSxProp) {
39+
return (
40+
<Box
41+
as={'div'}
42+
sx={sxProp}
43+
ref={forwardRef}
44+
className={clsx(className, classes.HeaderItem)}
45+
data-full={full}
46+
{...rest}
47+
>
48+
{children}
49+
</Box>
50+
)
51+
}
7852
return (
79-
<StyledHeaderItem
80-
ref={forwardRef}
81-
className={clsx(className, enabled && classes.HeaderItem)}
82-
data-full={rest.full}
83-
{...rest}
84-
>
53+
<div ref={forwardRef} className={clsx(className, classes.HeaderItem)} data-full={full} {...rest}>
8554
{children}
86-
</StyledHeaderItem>
55+
</div>
8756
)
8857
})
8958

9059
HeaderItem.displayName = 'Header.Item'
9160

92-
const StyledHeaderLink = toggleStyledComponent(
93-
CSS_MODULES_FEATURE_FLAG,
94-
'a',
95-
styled.a.attrs<StyledHeaderLinkProps>(({to}) => {
96-
const isReactRouter = typeof to === 'string'
97-
if (isReactRouter) {
98-
// according to their docs, NavLink supports aria-current:
99-
// https://reacttraining.com/react-router/web/api/NavLink/aria-current-string
100-
return {'aria-current': 'page'}
101-
} else {
102-
return {}
103-
}
104-
})<StyledHeaderLinkProps>`
105-
font-weight: ${get('fontWeights.bold')};
106-
color: ${get('colors.header.logo')};
107-
white-space: nowrap;
108-
cursor: pointer;
109-
text-decoration: none;
110-
display: flex;
111-
align-items: center;
112-
113-
&:hover,
114-
&:focus {
115-
color: ${get('colors.header.text')};
116-
}
117-
118-
${sx};
119-
`,
120-
)
121-
122-
const HeaderLink = React.forwardRef<HTMLElement, StyledHeaderLinkProps>(function HeaderLink(
123-
{children, className, ...rest},
61+
const HeaderLink = React.forwardRef<HTMLAnchorElement, HeaderLinkProps>(function HeaderLink(
62+
{children, className, sx: sxProp = defaultSxProp, as = 'a', ...rest},
12463
forwardRef,
12564
) {
126-
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
65+
if (sxProp !== defaultSxProp || as !== 'a') {
66+
return (
67+
<Box as={as} sx={sxProp} ref={forwardRef} className={clsx(className, classes.HeaderLink)} {...rest}>
68+
{children}
69+
</Box>
70+
)
71+
}
12772
return (
128-
<StyledHeaderLink ref={forwardRef} className={clsx(className, enabled && classes.HeaderLink)} {...rest}>
73+
<a ref={forwardRef} className={clsx(className, classes.HeaderLink)} {...rest}>
12974
{children}
130-
</StyledHeaderLink>
75+
</a>
13176
)
13277
})
13378

13479
HeaderLink.displayName = 'Header.Link'
13580

136-
export type HeaderProps = ComponentProps<typeof Header>
137-
export type HeaderLinkProps = ComponentProps<typeof HeaderLink>
138-
export type HeaderItemProps = ComponentProps<typeof HeaderItem>
13981
export default Object.assign(Header, {Link: HeaderLink, Item: HeaderItem})

‎packages/react/src/__tests__/Header.test.tsx

+1-41
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {Header} from '..'
33
import {render, behavesAsComponent, checkExports} from '../utils/testing'
44
import {render as HTMLRender} from '@testing-library/react'
55
import axe from 'axe-core'
6-
import {FeatureFlags} from '../FeatureFlags'
76

87
describe('Header', () => {
98
behavesAsComponent({Component: Header})
@@ -13,28 +12,15 @@ describe('Header', () => {
1312
})
1413

1514
describe('Header.Item', () => {
16-
behavesAsComponent({Component: Header.Item})
15+
behavesAsComponent({Component: Header.Item, options: {skipAs: true}})
1716

1817
it('accepts and applies className', () => {
1918
expect(render(<Header.Item className="primer" />).props.className).toContain('primer')
2019
})
2120

2221
it('should support `className` on the outermost element', () => {
2322
const Element = () => <Header.Item className={'test-class-name'} />
24-
const FeatureFlagElement = () => {
25-
return (
26-
<FeatureFlags
27-
flags={{
28-
primer_react_css_modules_staff: true,
29-
primer_react_css_modules_ga: true,
30-
}}
31-
>
32-
<Element />
33-
</FeatureFlags>
34-
)
35-
}
3623
expect(HTMLRender(<Element />).container.firstChild).toHaveClass('test-class-name')
37-
expect(HTMLRender(<FeatureFlagElement />).container.firstChild).toHaveClass('test-class-name')
3824
})
3925
})
4026

@@ -43,20 +29,7 @@ describe('Header', () => {
4329

4430
it('should support `className` on the outermost element', () => {
4531
const Element = () => <Header.Link className={'test-class-name'} />
46-
const FeatureFlagElement = () => {
47-
return (
48-
<FeatureFlags
49-
flags={{
50-
primer_react_css_modules_staff: true,
51-
primer_react_css_modules_ga: true,
52-
}}
53-
>
54-
<Element />
55-
</FeatureFlags>
56-
)
57-
}
5832
expect(HTMLRender(<Element />).container.firstChild).toHaveClass('test-class-name')
59-
expect(HTMLRender(<FeatureFlagElement />).container.firstChild).toHaveClass('test-class-name')
6033
})
6134
})
6235

@@ -87,19 +60,6 @@ describe('Header', () => {
8760

8861
it('should support `className` on the outermost element', () => {
8962
const Element = () => <Header className={'test-class-name'} />
90-
const FeatureFlagElement = () => {
91-
return (
92-
<FeatureFlags
93-
flags={{
94-
primer_react_css_modules_staff: true,
95-
primer_react_css_modules_ga: true,
96-
}}
97-
>
98-
<Element />
99-
</FeatureFlags>
100-
)
101-
}
10263
expect(HTMLRender(<Element />).container.firstChild).toHaveClass('test-class-name')
103-
expect(HTMLRender(<FeatureFlagElement />).container.firstChild).toHaveClass('test-class-name')
10464
})
10565
})

0 commit comments

Comments
 (0)
Please sign in to comment.