Skip to content

Commit

Permalink
chore(Header): Remove CSS modules feature flag from Header (#5510)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jonrohan authored Feb 14, 2025
1 parent 56baa0a commit 37a91b5
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 145 deletions.
5 changes: 5 additions & 0 deletions .changeset/shaggy-comics-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": minor
---

Remove CSS modules feature flag from Header
150 changes: 46 additions & 104 deletions packages/react/src/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,139 +1,81 @@
import type {Location, Pathname} from 'history'
import styled, {css} from 'styled-components'
import {get} from '../constants'
import type {SxProp} from '../sx'
import sx from '../sx'
import type {ComponentProps} from '../utils/types'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import {useFeatureFlag} from '../FeatureFlags'
import React from 'react'
import {clsx} from 'clsx'
import classes from './Header.module.css'
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
import {defaultSxProp} from '../utils/defaultSxProp'
import Box from '../Box'

type StyledHeaderProps = React.ComponentProps<'header'> & SxProp
type StyledHeaderItemProps = React.ComponentProps<'div'> & SxProp & {full?: boolean}
type StyledHeaderLinkProps = React.ComponentProps<'a'> & SxProp & {to?: Location | Pathname}
export type HeaderProps = React.ComponentProps<'header'> & SxProp & {as?: React.ElementType}
export type HeaderItemProps = React.ComponentProps<'div'> & SxProp & {full?: boolean}
export type HeaderLinkProps = React.ComponentProps<'a'> & SxProp & {to?: Location | Pathname; as?: React.ElementType}

const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_ga'

const StyledHeader = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'header',
styled.header<StyledHeaderProps>`
z-index: 32;
display: flex;
padding: ${get('space.3')};
font-size: ${get('fontSizes.1')};
line-height: ${get('lineHeights.default')};
color: ${get('colors.header.text')};
background-color: ${get('colors.header.bg')};
align-items: center;
flex-wrap: nowrap;
overflow: auto;
${sx};
`,
)

const Header = React.forwardRef<HTMLElement, StyledHeaderProps>(function Header(
{children, className, ...rest},
const Header = React.forwardRef<HTMLElement, HeaderProps>(function Header(
{children, className, sx: sxProp = defaultSxProp, as = 'header', ...rest},
forwardRef,
) {
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
if (sxProp !== defaultSxProp || as !== 'header') {
return (
<Box as={as} sx={sxProp} ref={forwardRef} className={clsx(className, classes.Header)} {...rest}>
{children}
</Box>
)
}
return (
<StyledHeader ref={forwardRef} className={clsx(className, {[classes.Header]: enabled})} {...rest}>
<header ref={forwardRef} className={clsx(className, classes.Header)} {...rest}>
{children}
</StyledHeader>
</header>
)
}) as PolymorphicForwardRefComponent<'header', StyledHeaderProps>
}) as PolymorphicForwardRefComponent<'header', HeaderProps>

Header.displayName = 'Header'

const StyledHeaderItem = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'div',
styled.div<StyledHeaderItemProps>`
display: flex;
margin-right: ${get('space.3')};
align-self: stretch;
align-items: center;
flex-wrap: nowrap;
${({full}) =>
full &&
css`
flex: auto;
`};
${sx};
`,
)

const HeaderItem = React.forwardRef<HTMLElement, StyledHeaderItemProps>(function HeaderItem(
{children, className, ...rest},
const HeaderItem = React.forwardRef<HTMLDivElement, HeaderItemProps>(function HeaderItem(
{children, className, sx: sxProp = defaultSxProp, full, ...rest},
forwardRef,
) {
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
if (sxProp !== defaultSxProp) {
return (
<Box
as={'div'}
sx={sxProp}
ref={forwardRef}
className={clsx(className, classes.HeaderItem)}
data-full={full}
{...rest}
>
{children}
</Box>
)
}
return (
<StyledHeaderItem
ref={forwardRef}
className={clsx(className, enabled && classes.HeaderItem)}
data-full={rest.full}
{...rest}
>
<div ref={forwardRef} className={clsx(className, classes.HeaderItem)} data-full={full} {...rest}>
{children}
</StyledHeaderItem>
</div>
)
})

HeaderItem.displayName = 'Header.Item'

const StyledHeaderLink = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'a',
styled.a.attrs<StyledHeaderLinkProps>(({to}) => {
const isReactRouter = typeof to === 'string'
if (isReactRouter) {
// according to their docs, NavLink supports aria-current:
// https://reacttraining.com/react-router/web/api/NavLink/aria-current-string
return {'aria-current': 'page'}
} else {
return {}
}
})<StyledHeaderLinkProps>`
font-weight: ${get('fontWeights.bold')};
color: ${get('colors.header.logo')};
white-space: nowrap;
cursor: pointer;
text-decoration: none;
display: flex;
align-items: center;
&:hover,
&:focus {
color: ${get('colors.header.text')};
}
${sx};
`,
)

const HeaderLink = React.forwardRef<HTMLElement, StyledHeaderLinkProps>(function HeaderLink(
{children, className, ...rest},
const HeaderLink = React.forwardRef<HTMLAnchorElement, HeaderLinkProps>(function HeaderLink(
{children, className, sx: sxProp = defaultSxProp, as = 'a', ...rest},
forwardRef,
) {
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
if (sxProp !== defaultSxProp || as !== 'a') {
return (
<Box as={as} sx={sxProp} ref={forwardRef} className={clsx(className, classes.HeaderLink)} {...rest}>
{children}
</Box>
)
}
return (
<StyledHeaderLink ref={forwardRef} className={clsx(className, enabled && classes.HeaderLink)} {...rest}>
<a ref={forwardRef} className={clsx(className, classes.HeaderLink)} {...rest}>
{children}
</StyledHeaderLink>
</a>
)
})

HeaderLink.displayName = 'Header.Link'

export type HeaderProps = ComponentProps<typeof Header>
export type HeaderLinkProps = ComponentProps<typeof HeaderLink>
export type HeaderItemProps = ComponentProps<typeof HeaderItem>
export default Object.assign(Header, {Link: HeaderLink, Item: HeaderItem})
42 changes: 1 addition & 41 deletions packages/react/src/__tests__/Header.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {Header} from '..'
import {render, behavesAsComponent, checkExports} from '../utils/testing'
import {render as HTMLRender} from '@testing-library/react'
import axe from 'axe-core'
import {FeatureFlags} from '../FeatureFlags'

describe('Header', () => {
behavesAsComponent({Component: Header})
Expand All @@ -13,28 +12,15 @@ describe('Header', () => {
})

describe('Header.Item', () => {
behavesAsComponent({Component: Header.Item})
behavesAsComponent({Component: Header.Item, options: {skipAs: true}})

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

it('should support `className` on the outermost element', () => {
const Element = () => <Header.Item className={'test-class-name'} />
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_staff: true,
primer_react_css_modules_ga: true,
}}
>
<Element />
</FeatureFlags>
)
}
expect(HTMLRender(<Element />).container.firstChild).toHaveClass('test-class-name')
expect(HTMLRender(<FeatureFlagElement />).container.firstChild).toHaveClass('test-class-name')
})
})

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

it('should support `className` on the outermost element', () => {
const Element = () => <Header.Link className={'test-class-name'} />
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_staff: true,
primer_react_css_modules_ga: true,
}}
>
<Element />
</FeatureFlags>
)
}
expect(HTMLRender(<Element />).container.firstChild).toHaveClass('test-class-name')
expect(HTMLRender(<FeatureFlagElement />).container.firstChild).toHaveClass('test-class-name')
})
})

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

it('should support `className` on the outermost element', () => {
const Element = () => <Header className={'test-class-name'} />
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_staff: true,
primer_react_css_modules_ga: true,
}}
>
<Element />
</FeatureFlags>
)
}
expect(HTMLRender(<Element />).container.firstChild).toHaveClass('test-class-name')
expect(HTMLRender(<FeatureFlagElement />).container.firstChild).toHaveClass('test-class-name')
})
})

0 comments on commit 37a91b5

Please sign in to comment.