Skip to content

Commit 4dd1d9d

Browse files
joshblackjonrohan
andauthored
feat: add createStyledComponent helper to styled-react package (#6588)
Co-authored-by: Jon Rohan <[email protected]>
1 parent 3da2704 commit 4dd1d9d

File tree

9 files changed

+141
-37
lines changed

9 files changed

+141
-37
lines changed

package-lock.json

Lines changed: 6 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {beforeEach} from 'vitest'
2+
import {cleanup} from '@testing-library/react'
3+
import '@testing-library/jest-dom/vitest'
4+
5+
beforeEach(() => {
6+
cleanup()
7+
})

packages/styled-react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@rollup/plugin-babel": "^6.0.4",
3232
"@types/react": "18.3.11",
3333
"@types/react-dom": "18.3.1",
34+
"@vitejs/plugin-react": "^4.3.3",
3435
"react": "18.3.1",
3536
"react-dom": "18.3.1",
3637
"rimraf": "^6.0.1",
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import {render, screen} from '@testing-library/react'
2+
import {describe, test, expect} from 'vitest'
3+
import {createStyledComponent} from '../createStyledComponent'
4+
5+
describe('createStyledComponent', () => {
6+
test('supports the sx prop', () => {
7+
const Wrapper = createStyledComponent(function Test(props: React.ComponentPropsWithoutRef<'div'>) {
8+
return <div {...props} data-testid="wrapper" />
9+
})
10+
11+
render(<Wrapper sx={{display: 'flex'}} />)
12+
13+
const wrapper = screen.getByTestId('wrapper')
14+
const style = window.getComputedStyle(wrapper)
15+
expect(style.display).toBe('flex')
16+
})
17+
18+
test('supports the as prop', () => {
19+
const Wrapper = createStyledComponent(function Test({
20+
as: BaseComponent,
21+
...rest
22+
}: {
23+
as: React.ElementType
24+
className?: string
25+
}) {
26+
return <BaseComponent {...rest} />
27+
})
28+
29+
render(<Wrapper as="section" data-testid="wrapper" sx={{display: 'flex'}} />)
30+
const wrapper = screen.getByTestId('wrapper')
31+
expect(wrapper.tagName).toBe('SECTION')
32+
33+
const style = window.getComputedStyle(wrapper)
34+
expect(style.display).toBe('flex')
35+
})
36+
37+
test('supports original component props on wrapper', () => {
38+
const Wrapper = createStyledComponent(function Test(props: {variant: 'a' | 'b'}) {
39+
return <div data-testid="wrapper" data-variant={props.variant} />
40+
})
41+
42+
render(<Wrapper variant="a" />)
43+
expect(screen.getByTestId('wrapper')).toHaveAttribute('data-variant', 'a')
44+
})
45+
})
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {sx} from '@primer/react'
2+
import type {SxProp} from '@primer/react'
3+
import type React from 'react'
4+
import styled from 'styled-components'
5+
import {background, border, color, flexbox, grid, layout, position, shadow, space, typography} from 'styled-system'
6+
import type {
7+
BackgroundProps,
8+
BorderProps,
9+
ColorProps,
10+
FlexboxProps,
11+
GridProps,
12+
LayoutProps,
13+
PositionProps,
14+
ShadowProps,
15+
SpaceProps,
16+
TypographyProps,
17+
} from 'styled-system'
18+
19+
type StyledBoxProps = SxProp &
20+
SpaceProps &
21+
ColorProps &
22+
TypographyProps &
23+
LayoutProps &
24+
FlexboxProps &
25+
GridProps &
26+
BackgroundProps &
27+
BorderProps &
28+
PositionProps &
29+
ShadowProps
30+
31+
/**
32+
* Utility that mirrors the functionality of the `Box` component from
33+
* `@primer/react`. Used to create a styled component variant of a component
34+
* from `@primer/react` that no longer supports `sx` or other styled-system
35+
* props.
36+
*
37+
* Note: make sure to include #__PURE__ when using this function to create a
38+
* component. For example:
39+
*
40+
* ```tsx
41+
* const Link = \/*#__PURE__*\/ createStyledComponent(PrimerLink)
42+
* ```
43+
*/
44+
export function createStyledComponent<P>(Component: React.ComponentType<P>) {
45+
return styled(Component)<StyledBoxProps>(
46+
space,
47+
color,
48+
typography,
49+
layout,
50+
flexbox,
51+
grid,
52+
background,
53+
border,
54+
position,
55+
shadow,
56+
sx,
57+
)
58+
}

packages/styled-react/tsconfig.build.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
"declaration": true,
66
"emitDeclarationOnly": true
77
},
8-
"exclude": ["vitest.config.ts"]
8+
"exclude": ["vitest.config.ts", "vitest.config.browser.ts"]
99
}

packages/styled-react/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"extends": "../../tsconfig.base.json",
3-
"include": ["src", "vitest.config.ts"]
3+
"include": ["src", "vitest.config.ts", "vitest.config.browser.ts", "config/vitest"]
44
}
55

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import react from '@vitejs/plugin-react'
2+
import {defineConfig} from 'vitest/config'
3+
4+
export default defineConfig({
5+
plugins: [react()],
6+
test: {
7+
include: ['src/**/*.browser.test.?(c|m)[jt]s?(x)'],
8+
setupFiles: ['config/vitest/browser/setup.ts'],
9+
browser: {
10+
provider: 'playwright',
11+
enabled: true,
12+
headless: process.env.DEBUG_BROWSER_TESTS === 'true' ? false : true,
13+
instances: [
14+
{
15+
browser: 'chromium',
16+
},
17+
],
18+
screenshotFailures: false,
19+
},
20+
},
21+
})

packages/styled-react/vitest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ import {defineConfig} from 'vitest/config'
33
export default defineConfig({
44
test: {
55
environment: 'node',
6+
exclude: ['src/**/*.browser.test.?(c|m)[jt]s?(x)'],
67
},
78
})

0 commit comments

Comments
 (0)