diff --git a/.changeset/afraid-eyes-serve.md b/.changeset/afraid-eyes-serve.md
new file mode 100644
index 00000000000..ff70b8317cf
--- /dev/null
+++ b/.changeset/afraid-eyes-serve.md
@@ -0,0 +1,5 @@
+---
+"@primer/styled-react": patch
+---
+
+chore: use forwardedAs prop in styled-react
diff --git a/packages/styled-react/src/__tests__/primer-react-deprecated.browser.test.tsx b/packages/styled-react/src/__tests__/primer-react-deprecated.browser.test.tsx
index dd0a9696a04..1913123d5f0 100644
--- a/packages/styled-react/src/__tests__/primer-react-deprecated.browser.test.tsx
+++ b/packages/styled-react/src/__tests__/primer-react-deprecated.browser.test.tsx
@@ -21,6 +21,7 @@ describe('@primer/react/deprecated', () => {
test('TabNav.Link supports `sx` prop', () => {
render()
+ expect(screen.getByTestId('component')).toHaveAttribute('role', 'tab')
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
expect(window.getComputedStyle(screen.getByRole('tab')).backgroundColor).toBe('rgb(255, 0, 0)')
expect(screen.getByRole('tab').tagName).toBe('BUTTON')
diff --git a/packages/styled-react/src/__tests__/primer-react-experimental.browser.test.tsx b/packages/styled-react/src/__tests__/primer-react-experimental.browser.test.tsx
index 4e2a8fa64fe..86ea2df3afd 100644
--- a/packages/styled-react/src/__tests__/primer-react-experimental.browser.test.tsx
+++ b/packages/styled-react/src/__tests__/primer-react-experimental.browser.test.tsx
@@ -9,7 +9,8 @@ describe('@primer/react/experimental', () => {
})
test('PageHeader supports `sx` prop', () => {
- const {container} = render()
+ const {container} = render()
+ expect(container.firstElementChild!).toHaveAttribute('role', 'article')
expect(window.getComputedStyle(container.firstElementChild!).backgroundColor).toBe('rgb(255, 0, 0)')
})
diff --git a/packages/styled-react/src/__tests__/primer-react.browser.test.tsx b/packages/styled-react/src/__tests__/primer-react.browser.test.tsx
index 7c179eb7ee7..33ed2c72e22 100644
--- a/packages/styled-react/src/__tests__/primer-react.browser.test.tsx
+++ b/packages/styled-react/src/__tests__/primer-react.browser.test.tsx
@@ -47,8 +47,9 @@ import {
describe('@primer/react', () => {
test('ActionList supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveAttribute('data-variant', 'inset')
})
test('ActionMenu.Button supports `sx` prop', () => {
@@ -109,7 +110,7 @@ describe('@primer/react', () => {
})
test('Box supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
})
@@ -119,14 +120,15 @@ describe('@primer/react', () => {
})
test('Breadcrumbs.Item supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
- expect(window.getComputedStyle(screen.getByRole('link')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component').className.includes('selected')).toBe(true)
})
test('Button supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveAttribute('data-size', 'medium')
})
test('Checkbox supports `sx` prop', () => {
@@ -184,8 +186,9 @@ describe('@primer/react', () => {
})
test('Flash supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveAttribute('variant', 'success')
})
test('FormControl supports `sx` prop', () => {
@@ -198,7 +201,7 @@ describe('@primer/react', () => {
})
test('Header supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
})
@@ -208,23 +211,40 @@ describe('@primer/react', () => {
})
test('IconButton supports `sx` prop', () => {
- render( } />)
+ render(
+ }
+ />,
+ )
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+
+ // Test that IconButton renders the icon component (SVG) in its children
+ const iconButton = screen.getByTestId('component')
+ const svgElement = iconButton.querySelector('svg')
+ expect(svgElement).toBeInTheDocument()
+ expect(iconButton.children.length).toBeGreaterThan(0)
})
test('Label supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveAttribute('data-size', 'large')
})
test('Link supports `sx` prop', () => {
- render()
+ render()
+ expect(screen.getByTestId('component')).toHaveAttribute('data-inline', 'true')
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
})
test('LinkButton supports `sx` prop', () => {
- render()
+ render(} />)
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveAttribute('icon')
})
test('NavList supports `sx` prop', () => {
@@ -286,19 +306,23 @@ describe('@primer/react', () => {
render(
{}}
onEscape={() => {}}
returnFocusRef={ref}
+ role="dialog"
/>
,
)
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveAttribute('role', 'dialog')
})
test('PageHeader supports `sx` prop', () => {
- const {container} = render()
+ const {container} = render()
+ expect(container.firstElementChild!).toHaveAttribute('role', 'article')
expect(window.getComputedStyle(container.firstElementChild!).backgroundColor).toBe('rgb(255, 0, 0)')
})
@@ -323,8 +347,13 @@ describe('@primer/react', () => {
})
test('PageLayout.Content supports `sx` prop', () => {
- const {container} = render()
- expect(window.getComputedStyle(container.firstElementChild!).backgroundColor).toBe('rgb(255, 0, 0)')
+ const {container} = render(
+ ,
+ )
+
+ const outerElement = container.firstElementChild! as HTMLElement
+ expect(window.getComputedStyle(outerElement).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(outerElement).toHaveAttribute('aria-labelledby', 'normal')
})
test('PageLayout.Pane supports `sx` prop', () => {
@@ -386,8 +415,9 @@ describe('@primer/react', () => {
})
test.skip('Select supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveAttribute('required')
})
test('Spinner supports `sx` prop', () => {
@@ -411,13 +441,15 @@ describe('@primer/react', () => {
})
test('Text supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveAttribute('data-size', 'small')
})
test('TextInput supports `sx` prop', () => {
- const {container} = render()
+ const {container} = render()
expect(window.getComputedStyle(container.firstElementChild!).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(container.firstElementChild).toHaveAttribute('data-trailing-visual', 'true')
})
test('TextInput.Action supports `sx` prop', () => {
@@ -456,8 +488,9 @@ describe('@primer/react', () => {
})
test('Token supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveTextContent('test')
})
test.todo('Tooltip supports `sx` prop', () => {
@@ -470,25 +503,29 @@ describe('@primer/react', () => {
})
test('Truncate supports `sx` prop', () => {
- render()
+ render()
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByTestId('component')).toHaveAttribute('title', 'test')
})
test('UnderlineNav supports `sx` prop', () => {
render(
-
+
test
,
)
expect(window.getComputedStyle(screen.getByLabelText('navigation')).backgroundColor).toBe('rgb(255, 0, 0)')
+ expect(screen.getByLabelText('navigation')).toHaveAttribute('data-variant', 'inset')
})
test('UnderlineNav.Item supports `sx` prop', () => {
render(
-
+ }>
test
,
)
expect(window.getComputedStyle(screen.getByTestId('component')).backgroundColor).toBe('rgb(255, 0, 0)')
+ const svgElement = screen.getByTestId('component').querySelector('svg')
+ expect(svgElement).toBeInTheDocument()
})
})
diff --git a/packages/styled-react/src/components/Breadcrumbs.tsx b/packages/styled-react/src/components/Breadcrumbs.tsx
index 97a5c2d3fc1..a675400de6a 100644
--- a/packages/styled-react/src/components/Breadcrumbs.tsx
+++ b/packages/styled-react/src/components/Breadcrumbs.tsx
@@ -25,9 +25,9 @@ const StyledBreadcrumbsItem: ForwardRefComponent<'a', BreadcrumbsItemProps> = st
${sx}
`
-const BreadcrumbsItem = ({as, ...props}: BreadcrumbsItemProps) => (
-
-)
+function BreadcrumbsItem({as, ...props}: BreadcrumbsItemProps) {
+ return
+}
const Breadcrumbs: ForwardRefComponent<'nav', BreadcrumbsProps> & {Item: typeof BreadcrumbsItem} = Object.assign(
BreadcrumbsImpl,
diff --git a/packages/styled-react/src/components/Header.tsx b/packages/styled-react/src/components/Header.tsx
index b7266645a53..da906c45477 100644
--- a/packages/styled-react/src/components/Header.tsx
+++ b/packages/styled-react/src/components/Header.tsx
@@ -3,6 +3,7 @@ import {
type HeaderItemProps as PrimerHeaderItemProps,
type HeaderLinkProps as PrimerHeaderLinkProps,
Header as PrimerHeader,
+ type HeaderLinkProps,
} from '@primer/react'
import {forwardRef} from 'react'
import {Box} from './Box'
@@ -11,20 +12,28 @@ import type {SxProp} from '../sx'
type HeaderProps = PrimerHeaderProps & SxProp
-const HeaderImpl = forwardRef(function Header(props, ref) {
+const StyledHeader = forwardRef(function Header(props, ref) {
return
}) as ForwardRefComponent<'header', HeaderProps>
+const HeaderImpl = forwardRef(({as, ...props}: HeaderProps, ref) => (
+
+)) as ForwardRefComponent<'header', HeaderProps>
+
type HeaderItemProps = PrimerHeaderItemProps & SxProp
const HeaderItem = forwardRef(function HeaderItem(props, ref) {
return
})
-const HeaderLink = forwardRef(function HeaderLink(props, ref) {
+const StyledHeaderLink = forwardRef(function HeaderLink(props, ref) {
return
})
+const HeaderLink = forwardRef(({as, ...props}, ref) => (
+
+))
+
const Header = Object.assign(HeaderImpl, {
Item: HeaderItem,
Link: HeaderLink,
diff --git a/packages/styled-react/src/components/Label.tsx b/packages/styled-react/src/components/Label.tsx
index d840ce19695..f62ed44e99a 100644
--- a/packages/styled-react/src/components/Label.tsx
+++ b/packages/styled-react/src/components/Label.tsx
@@ -3,10 +3,14 @@ import {type SxProp} from '../sx'
import {forwardRef} from 'react'
import type {ForwardRefComponent} from '../polymorphic'
-type LabelProps = PrimerLabelProps & SxProp
+type LabelProps = PrimerLabelProps & SxProp & {as?: React.ElementType}
-const Label = forwardRef(function Label(props, ref) {
+const StyledLabel = forwardRef(function Label(props, ref) {
return
}) as ForwardRefComponent<'span', LabelProps>
+const Label = forwardRef(({as, ...props}, ref) => {
+ return
+}) as ForwardRefComponent<'span', LabelProps>
+
export {Label, type LabelProps}
diff --git a/packages/styled-react/src/components/Link.tsx b/packages/styled-react/src/components/Link.tsx
index c87a2629684..f87879d9a09 100644
--- a/packages/styled-react/src/components/Link.tsx
+++ b/packages/styled-react/src/components/Link.tsx
@@ -1,12 +1,19 @@
import {Link as PrimerLink, type LinkProps as PrimerLinkProps} from '@primer/react'
import styled from 'styled-components'
import {sx, type SxProp} from '../sx'
+import type {ForwardRefComponent} from '../polymorphic'
+import {forwardRef} from 'react'
type LinkProps = PrimerLinkProps & SxProp
-const Link = styled(PrimerLink).withConfig({
+const StyledLink = styled(PrimerLink).withConfig({
shouldForwardProp: prop => prop !== 'sx',
})`
${sx}
-`
+` as ForwardRefComponent<'a', LinkProps>
+
+const Link = forwardRef(({as, ...props}, ref) => {
+ return
+}) as ForwardRefComponent<'a', LinkProps>
+
export {Link, type LinkProps}
diff --git a/packages/styled-react/src/components/PageHeader.tsx b/packages/styled-react/src/components/PageHeader.tsx
index db23410c7c7..4b099c64d1c 100644
--- a/packages/styled-react/src/components/PageHeader.tsx
+++ b/packages/styled-react/src/components/PageHeader.tsx
@@ -10,10 +10,11 @@ import {sx, type SxProp} from '../sx'
import type {ForwardRefComponent} from '../polymorphic'
import {Box} from './Box'
import type {PropsWithChildren} from 'react'
+import React from 'react'
type PageHeaderProps = PrimerPageHeaderProps & SxProp
-const PageHeaderImpl: ForwardRefComponent<'div', PageHeaderProps> = styled(
+const StyledPageHeader: ForwardRefComponent<'div', PageHeaderProps> = styled(
PrimerPageHeader,
).withConfig({
shouldForwardProp: prop => prop !== 'sx',
@@ -21,6 +22,10 @@ const PageHeaderImpl: ForwardRefComponent<'div', PageHeaderProps> = styled(
${sx}
`
+const PageHeaderImpl = React.forwardRef(({as, ...props}, ref) => (
+
+)) as ForwardRefComponent<'div', PageHeaderProps>
+
type PageHeaderActionsProps = PrimerPageHeaderActionsProps & SxProp
function PageHeaderActions({sx, ...rest}: PageHeaderActionsProps) {
@@ -43,7 +48,7 @@ type CSSCustomProperties = {
[key: `--${string}`]: string | number
}
-function PageHeaderTitle({sx, ...rest}: PageHeaderTitleProps) {
+function StyledPageHeaderTitle({sx, ...rest}: PageHeaderTitleProps) {
const style: CSSCustomProperties = {}
if (sx) {
// @ts-ignore sx can have color attribute
@@ -65,6 +70,10 @@ function PageHeaderTitle({sx, ...rest}: PageHeaderTitleProps) {
return
}
+const PageHeaderTitle = ({as, ...props}: PageHeaderTitleProps) => (
+
+)
+
type PageHeaderTitleAreaProps = PropsWithChildren & SxProp
const PageHeaderTitleArea: ForwardRefComponent<'div', PageHeaderTitleAreaProps> = styled(
@@ -75,7 +84,7 @@ const PageHeaderTitleArea: ForwardRefComponent<'div', PageHeaderTitleAreaProps>
${sx}
`
-type PageHeaderComponent = ForwardRefComponent<'div', PageHeaderProps> & {
+type PageHeaderComponentType = ForwardRefComponent<'div', PageHeaderProps> & {
Actions: typeof PageHeaderActions
ContextArea: typeof PrimerPageHeader.ContextArea
ParentLink: typeof PrimerPageHeader.ParentLink
@@ -91,7 +100,7 @@ type PageHeaderComponent = ForwardRefComponent<'div', PageHeaderProps> & {
TrailingAction: typeof PrimerPageHeader.TrailingAction
}
-const PageHeader: PageHeaderComponent = Object.assign(PageHeaderImpl, {
+const PageHeader: PageHeaderComponentType = Object.assign(PageHeaderImpl, {
Actions: PageHeaderActions,
ContextArea: PrimerPageHeader.ContextArea,
ParentLink: PrimerPageHeader.ParentLink,
diff --git a/packages/styled-react/src/components/StateLabelProps.tsx b/packages/styled-react/src/components/StateLabelProps.tsx
deleted file mode 100644
index 2d08f8c79d4..00000000000
--- a/packages/styled-react/src/components/StateLabelProps.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import {StateLabel as PrimerStateLabel, type StateLabelProps as PrimerStateLabelProps} from '@primer/react'
-import {Box} from './Box'
-import {forwardRef} from 'react'
-import {type SxProp} from '../sx'
-
-export type StateLabelProps = PrimerStateLabelProps & SxProp
-
-export const StateLabel = forwardRef(function StateLabel(props, ref) {
- return
-})
diff --git a/packages/styled-react/src/components/TabNav.tsx b/packages/styled-react/src/components/TabNav.tsx
index 0ac8c3d7634..961d66ee51f 100644
--- a/packages/styled-react/src/components/TabNav.tsx
+++ b/packages/styled-react/src/components/TabNav.tsx
@@ -2,7 +2,7 @@ import {TabNav as PrimerTabNav} from '@primer/react/deprecated'
import type {TabNavProps as PrimerTabNavProps, TabNavLinkProps as PrimerTabNavLinkProps} from '@primer/react/deprecated'
import {sx, type SxProp} from '../sx'
import styled from 'styled-components'
-import {type ForwardRefComponent} from '../polymorphic'
+import {forwardRef} from 'react'
type TabNavProps = PrimerTabNavProps & SxProp
type TabNavLinkProps = PrimerTabNavLinkProps & SxProp
@@ -17,15 +17,15 @@ const TabNavImpl = ({as, ...props}: TabNavProps) => {
return
}
-const StyledTabNavLink: ForwardRefComponent<'a', TabNavLinkProps> = styled(PrimerTabNav.Link).withConfig({
+const StyledTabNavLink = styled(PrimerTabNav.Link).withConfig({
shouldForwardProp: prop => (prop as keyof TabNavLinkProps) !== 'sx',
})`
${sx}
`
-const TabNavLink = ({as, ...props}: TabNavLinkProps) => (
-
-)
+const TabNavLink = forwardRef(({as, ...props}, ref) => (
+
+))
const TabNav = Object.assign(TabNavImpl, {
Link: TabNavLink,
diff --git a/packages/styled-react/src/components/UnderlineNav.tsx b/packages/styled-react/src/components/UnderlineNav.tsx
index e4bec07908b..8ccbb31a960 100644
--- a/packages/styled-react/src/components/UnderlineNav.tsx
+++ b/packages/styled-react/src/components/UnderlineNav.tsx
@@ -11,19 +11,26 @@ import {sx, type SxProp} from '../sx'
export type UnderlineNavProps = PrimerUnderlineNavProps & SxProp
-const UnderlineNavImpl = forwardRef(function UnderlineNav(props, ref) {
+const StyledUnderlineNav = forwardRef(function UnderlineNav(props, ref) {
return
})
-export type UnderlineNavItemProps = PrimerUnderlineNavItemProps & SxProp
+export const UnderlineNavImpl = forwardRef(({as, ...props}: UnderlineNavProps, ref) => (
+
+)) as ForwardRefComponent<'nav', UnderlineNavProps>
-const UnderlineNavItem: ForwardRefComponent<'a', UnderlineNavItemProps> = styled(
+export type UnderlineNavItemProps = PrimerUnderlineNavItemProps & SxProp & React.HTMLAttributes
+
+const StyledUnderlineNavItem: ForwardRefComponent<'a', UnderlineNavItemProps> = styled(
PrimerUnderlineNav.Item,
).withConfig({
shouldForwardProp: prop => prop !== 'sx',
})`
${sx}
`
+export const UnderlineNavItem = forwardRef(({as, ...props}: UnderlineNavItemProps, ref) => (
+
+)) as ForwardRefComponent<'a', UnderlineNavItemProps>
export const UnderlineNav = Object.assign(UnderlineNavImpl, {
Item: UnderlineNavItem,