Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/three-jokes-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

Make sure all components accept `className` as a prop on outermost component element.
19 changes: 17 additions & 2 deletions packages/react/src/Banner/Banner.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import React from 'react'
import {Banner} from '../Banner'
import {FeatureFlags} from '../FeatureFlags'

describe('Banner', () => {
let spy: jest.SpyInstance
Expand Down Expand Up @@ -30,8 +31,22 @@ describe('Banner', () => {
})

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

it('should label the landmark element with the corresponding variant label text', () => {
Expand Down
20 changes: 20 additions & 0 deletions packages/react/src/Breadcrumbs/__tests__/Breadcrumbs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Breadcrumbs, {Breadcrumb} 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('Breadcrumbs', () => {
behavesAsComponent({Component: Breadcrumbs, options: {skipAs: true}})
Expand All @@ -12,6 +13,25 @@ describe('Breadcrumbs', () => {
Breadcrumb,
})

it('should support `className` on the outermost element', () => {
const Element = () => <Breadcrumbs className={'test-class-name'} />
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
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')
})

it('should have no axe violations', async () => {
const {container} = HTMLRender(<Breadcrumbs />)
const results = await axe.run(container)
Expand Down
19 changes: 19 additions & 0 deletions packages/react/src/Button/__tests__/Button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,25 @@ describe('Button', () => {
options: {skipSx: true, skipAs: true},
})

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

it('renders a <button>', () => {
const container = render(<Button id="test-button">Default</Button>)
const button = container.getByRole('button')
Expand Down
20 changes: 20 additions & 0 deletions packages/react/src/Checkbox/Checkbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event'
import React from 'react'
import Checkbox from '../Checkbox'
import {behavesAsComponent, checkExports} from '../utils/testing'
import {FeatureFlags} from '../FeatureFlags'

describe('Checkbox', () => {
beforeEach(() => {
Expand All @@ -14,6 +15,25 @@ describe('Checkbox', () => {
default: Checkbox,
})

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

it('renders a valid checkbox input', () => {
const {getByRole} = render(<Checkbox />)

Expand Down
20 changes: 20 additions & 0 deletions packages/react/src/CounterLabel/CounterLabel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {CounterLabel} from '..'
import {behavesAsComponent, checkExports} from '../utils/testing'
import {render as HTMLRender} from '@testing-library/react'
import axe from 'axe-core'
import {FeatureFlags} from '../FeatureFlags'

describe('CounterLabel', () => {
behavesAsComponent({Component: CounterLabel, options: {skipAs: true, skipSx: true}})
Expand All @@ -11,6 +12,25 @@ describe('CounterLabel', () => {
default: CounterLabel,
})

it('should support `className` on the outermost element', () => {
const Element = () => <CounterLabel className={'test-class-name'} />
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
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')
})

it('renders a <span>', () => {
const {container} = HTMLRender(<CounterLabel>1234</CounterLabel>)
expect(container.firstChild?.nodeName).toEqual('SPAN')
Expand Down
25 changes: 20 additions & 5 deletions packages/react/src/Heading/__tests__/Heading.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {render, behavesAsComponent, checkExports} from '../../utils/testing'
import {render as HTMLRender, screen} from '@testing-library/react'
import axe from 'axe-core'
import ThemeProvider from '../../ThemeProvider'
import {FeatureFlags} from '../../FeatureFlags'

const theme = {
breakpoints: ['400px', '640px', '960px', '1280px'],
Expand Down Expand Up @@ -35,6 +36,25 @@ describe('Heading', () => {
default: Heading,
})

it('should support `className` on the outermost element', () => {
const Element = () => <Heading className={'test-class-name'} />
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
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')
})

it('renders <h2> by default', () => {
expect(render(<Heading />).type).toEqual('h2')
})
Expand Down Expand Up @@ -149,11 +169,6 @@ describe('Heading', () => {
expect(screen.getByText('test')).not.toHaveClass(/^Heading__StyledHeading/)
})

it('should support `className` on the outermost element', () => {
const {container} = HTMLRender(<Heading className="test">test</Heading>)
expect(container.firstChild).toHaveClass('test')
})

it('should support overrides with sx if provided', () => {
HTMLRender(
<Heading
Expand Down
20 changes: 20 additions & 0 deletions packages/react/src/Link/__tests__/Link.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Link 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('Link', () => {
behavesAsComponent({Component: Link})
Expand All @@ -11,6 +12,25 @@ describe('Link', () => {
default: Link,
})

it('should support `className` on the outermost element', () => {
const Element = () => <Link href="#" className={'test-class-name'} />
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
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')
})

it('should have no axe violations', async () => {
const {container} = HTMLRender(<Link href="www.github.com">GitHub</Link>)
const results = await axe.run(container)
Expand Down
10 changes: 9 additions & 1 deletion packages/react/src/Spinner/Spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,17 @@ export type SpinnerProps = {
srText?: string | null
/** @deprecated Use `srText` instead. */
'aria-label'?: string
className?: string
} & HTMLDataAttributes &
SxProp

function Spinner({size: sizeKey = 'medium', srText = 'Loading', 'aria-label': ariaLabel, ...props}: SpinnerProps) {
function Spinner({
size: sizeKey = 'medium',
srText = 'Loading',
'aria-label': ariaLabel,
className,
...props
}: SpinnerProps) {
const size = sizeMap[sizeKey]
const hasHiddenLabel = srText !== null && ariaLabel === undefined
const labelId = useId()
Expand All @@ -38,6 +45,7 @@ function Spinner({size: sizeKey = 'medium', srText = 'Loading', 'aria-label': ar
aria-hidden
aria-label={ariaLabel ?? undefined}
aria-labelledby={hasHiddenLabel ? labelId : undefined}
className={className}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the only component I added className support to, the rest already had it

{...props}
>
<circle
Expand Down
20 changes: 20 additions & 0 deletions packages/react/src/__tests__/Avatar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import theme from '../theme'
import {px, render, behavesAsComponent, checkExports} from '../utils/testing'
import {render as HTMLRender} from '@testing-library/react'
import axe from 'axe-core'
import {FeatureFlags} from '../FeatureFlags'

describe('Avatar', () => {
behavesAsComponent({Component: Avatar})
Expand All @@ -12,6 +13,25 @@ describe('Avatar', () => {
default: Avatar,
})

it('should support `className` on the outermost element', () => {
const Element = () => <Avatar src="primer.png" className={'test-class-name'} />
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
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')
})

it('should have no axe violations', async () => {
const {container} = HTMLRender(<Avatar src="primer.png" />)
const results = await axe.run(container)
Expand Down
27 changes: 27 additions & 0 deletions packages/react/src/__tests__/AvatarStack.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {AvatarStack} 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'

const avatarComp = (
<AvatarStack>
Expand Down Expand Up @@ -33,6 +34,32 @@ describe('Avatar', () => {
default: AvatarStack,
})

it('should support `className` on the outermost element', () => {
const Element = () => (
<AvatarStack className={'test-class-name'}>
<img src="https://avatars.githubusercontent.com/primer" alt="" />
<img src="https://avatars.githubusercontent.com/github" alt="" />
<img src="https://avatars.githubusercontent.com/primer" alt="" />
<img src="https://avatars.githubusercontent.com/github" alt="" />
</AvatarStack>
)
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
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')
})

it('should have no axe violations', async () => {
const {container} = HTMLRender(avatarComp)
const results = await axe.run(container)
Expand Down
20 changes: 20 additions & 0 deletions packages/react/src/__tests__/Box.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react'
import {Box} from '..'
import theme from '../theme'
import {behavesAsComponent, checkExports, render} from '../utils/testing'
import {FeatureFlags} from '../FeatureFlags'

describe('Box', () => {
behavesAsComponent({Component: Box})
Expand All @@ -12,6 +13,25 @@ describe('Box', () => {
default: Box,
})

it('should support `className` on the outermost element', () => {
const Element = () => <Box className={'test-class-name'} />
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
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')
})

it('should have no axe violations', async () => {
const {container} = HTMLRender(<Box />)
const results = await axe.run(container)
Expand Down
Loading
Loading