Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
45 changes: 8 additions & 37 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions packages/styled-react/config/vitest/browser/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {beforeEach} from 'vitest'
import {cleanup} from '@testing-library/react'
import '@testing-library/jest-dom/vitest'

beforeEach(() => {
cleanup()
})
1 change: 1 addition & 0 deletions packages/styled-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@rollup/plugin-babel": "^6.0.4",
"@types/react": "18.3.11",
"@types/react-dom": "18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"react": "18.3.1",
"react-dom": "18.3.1",
"rimraf": "^6.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {render, screen} from '@testing-library/react'
import {describe, test, expect} from 'vitest'
import {createStyledComponent} from '../createStyledComponent'

describe('createStyledComponent', () => {
test('supports the sx prop', () => {
const Wrapper = createStyledComponent(function Test(props: React.ComponentPropsWithoutRef<'div'>) {
return <div {...props} data-testid="wrapper" />
})

render(<Wrapper sx={{display: 'flex'}} />)

const wrapper = screen.getByTestId('wrapper')
const style = window.getComputedStyle(wrapper)
expect(style.display).toBe('flex')
})

test('supports the as prop', () => {
const Wrapper = createStyledComponent(function Test({
as: BaseComponent,
...rest
}: {
as: React.ElementType
className?: string
}) {
return <BaseComponent {...rest} />
})

render(<Wrapper as="section" data-testid="wrapper" sx={{display: 'flex'}} />)
const wrapper = screen.getByTestId('wrapper')
expect(wrapper.tagName).toBe('SECTION')

const style = window.getComputedStyle(wrapper)
expect(style.display).toBe('flex')
})

test('supports original component props on wrapper', () => {
const Wrapper = createStyledComponent(function Test(props: {variant: 'a' | 'b'}) {
return <div data-testid="wrapper" data-variant={props.variant} />
})

render(<Wrapper variant="a" />)
expect(screen.getByTestId('wrapper')).toHaveAttribute('data-variant', 'a')
})
})
58 changes: 58 additions & 0 deletions packages/styled-react/src/utils/createStyledComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {sx} from '@primer/react'
import type {SxProp} from '@primer/react'
import type React from 'react'
import styled from 'styled-components'
import {background, border, color, flexbox, grid, layout, position, shadow, space, typography} from 'styled-system'
import type {
BackgroundProps,
BorderProps,
ColorProps,
FlexboxProps,
GridProps,
LayoutProps,
PositionProps,
ShadowProps,
SpaceProps,
TypographyProps,
} from 'styled-system'

type StyledBoxProps = SxProp &
SpaceProps &
ColorProps &
TypographyProps &
LayoutProps &
FlexboxProps &
GridProps &
BackgroundProps &
BorderProps &
PositionProps &
ShadowProps

/**
* Utility that mirrors the functionality of the `Box` component from
* `@primer/react`. Used to create a styled component variant of a component
* from `@primer/react` that no longer supports `sx` or other styled-system
* props.
*
* Note: make sure to include #__PURE__ when using this function to create a
* component. For example:
*
* ```tsx
* const Link = \/*#__PURE__*\/ createStyledComponent(PrimerLink)
Copy link
Preview

Copilot AI Aug 12, 2025

Choose a reason for hiding this comment

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

The comment contains an escaped forward slash that makes the example code invalid. Remove the backslash to show the correct usage: /*#__PURE__*/

Copilot uses AI. Check for mistakes.

* ```
*/
export function createStyledComponent<P>(Component: React.ComponentType<P>) {
return styled(Component)<StyledBoxProps>(
space,
color,
typography,
layout,
flexbox,
grid,
background,
border,
position,
shadow,
sx,
)
}
2 changes: 1 addition & 1 deletion packages/styled-react/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src", "vitest.config.ts"]
"include": ["src", "vitest.config.ts", "config/vitest"]
}

21 changes: 21 additions & 0 deletions packages/styled-react/vitest.config.browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import react from '@vitejs/plugin-react'
import {defineConfig} from 'vitest/config'

export default defineConfig({
plugins: [react()],
test: {
include: ['src/**/*.browser.test.?(c|m)[jt]s?(x)'],
setupFiles: ['config/vitest/browser/setup.ts'],
browser: {
provider: 'playwright',
enabled: true,
headless: process.env.DEBUG_BROWSER_TESTS === 'true' ? false : true,
Copy link
Preview

Copilot AI Aug 12, 2025

Choose a reason for hiding this comment

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

[nitpick] The conditional can be simplified to headless: process.env.DEBUG_BROWSER_TESTS !== 'true' for better readability.

Suggested change
headless: process.env.DEBUG_BROWSER_TESTS === 'true' ? false : true,
headless: process.env.DEBUG_BROWSER_TESTS !== 'true',

Copilot uses AI. Check for mistakes.

instances: [
{
browser: 'chromium',
},
],
screenshotFailures: false,
},
},
})
1 change: 1 addition & 0 deletions packages/styled-react/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import {defineConfig} from 'vitest/config'
export default defineConfig({
test: {
environment: 'node',
exclude: ['src/**/*.browser.test.?(c|m)[jt]s?(x)'],
},
})
Loading