Skip to content

components: Remove style props #2326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
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
74 changes: 3 additions & 71 deletions packages/components/src/Box.tsx
Original file line number Diff line number Diff line change
@@ -2,60 +2,14 @@ import {
ArrayInterpolation,
CSSObject,
Interpolation,
jsx,
useTheme,
} from '@emotion/react'
import React, { forwardRef } from 'react'
import {
css,
get,
ThemeUICSSProperties,
ThemeUIStyleObject,
} from '@theme-ui/css'
import { css, get, ThemeUIStyleObject } from '@theme-ui/css'
import type { Assign, ForwardRef } from './types'
import type { __ThemeUIComponentsInternalProps } from './util'

const boxSystemProps = [
// space scale props (inherited from @styled-system/space)
'margin',
'marginTop',
'marginRight',
'marginBottom',
'marginLeft',
'marginX',
'marginY',
'm',
'mt',
'mr',
'mb',
'ml',
'mx',
'my',
'padding',
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft',
'paddingX',
'paddingY',
'p',
'pt',
'pr',
'pb',
'pl',
'px',
'py',
// color props (inherited from @styled-system/color)
'color',
'backgroundColor',
'bg',
'opacity',
] as const

type BoxSystemPropsKeys = (typeof boxSystemProps)[number]
type BoxSystemProps = Pick<ThemeUICSSProperties, BoxSystemPropsKeys>

export interface BoxOwnProps extends BoxSystemProps {
export interface BoxOwnProps {
as?: React.ElementType
variant?: string
css?: Interpolation<any>
@@ -68,22 +22,6 @@ export interface BoxProps
'ref'
> {}

/**
* @internal
*/
export const __isBoxStyledSystemProp = (prop: string) =>
(boxSystemProps as readonly string[]).includes(prop)

const pickSystemProps = (props: BoxOwnProps) => {
const res: Partial<Pick<BoxOwnProps, (typeof boxSystemProps)[number]>> = {}
for (const key of boxSystemProps) {
// ts2590: union is too large
;(res as any)[key] = props[key]
}

return res
}

/**
* Use the Box component as a layout primitive to add margin, padding, and colors to content.
* @see https://theme-ui.com/components/box
@@ -116,21 +54,15 @@ export const Box: ForwardRef<any, BoxProps> = forwardRef<any, BoxProps>(

const sxPropStyles = css(sx)(theme)

const systemPropsStyles = css(pickSystemProps(rest))(theme)

const style: ArrayInterpolation<unknown> = [
baseStyles,
__cssStyles,
variantStyles,
sxPropStyles,
systemPropsStyles,

cssProp,
]

boxSystemProps.forEach((name) => {
delete (rest as Record<string, unknown>)[name]
})

return <Component ref={ref} css={style} {...rest} />
}
)
11 changes: 7 additions & 4 deletions packages/components/src/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -2,7 +2,8 @@ import React from 'react'

import { Box, BoxOwnProps } from './Box'
import { SVG, SVGProps } from './SVG'
import { Assign, ForwardRef } from './types'
import type { Assign, ForwardRef } from './types'
import type { ThemeUICSSObject } from '@theme-ui/css'
import { __internalProps } from './util'

const CheckboxChecked = (props: SVGProps) => (
@@ -45,7 +46,9 @@ const CheckboxIcon = (props: SVGProps) => (
)

export interface CheckboxProps
extends Assign<React.ComponentPropsWithRef<'input'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'input'>, BoxOwnProps> {
containerSx?: ThemeUICSSObject
}

/**
* Form checkbox input component
@@ -56,11 +59,11 @@ export interface CheckboxProps
*/
export const Checkbox: ForwardRef<HTMLInputElement, CheckboxProps> =
React.forwardRef(function Checkbox(
{ className, sx, variant = 'checkbox', children, ...props },
{ className, sx, containerSx, variant = 'checkbox', children, ...props },
ref
) {
return (
<Box sx={{ minWidth: 'min-content' }}>
<Box sx={{ minWidth: 'min-content', ...containerSx }}>
<Box
ref={ref}
as="input"
10 changes: 3 additions & 7 deletions packages/components/src/Embed.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import React, { ComponentPropsWithoutRef } from 'react'

import { Box, BoxOwnProps, __isBoxStyledSystemProp } from './Box'
import { Box, BoxOwnProps } from './Box'
import { Assign, ForwardRef } from './types'
import { getProps, __internalProps } from './util'

const getContainerProps = getProps(__isBoxStyledSystemProp)
const getIframeProps = getProps((str) => !__isBoxStyledSystemProp(str))
import { __internalProps } from './util'

export interface EmbedProps
extends Assign<React.ComponentPropsWithRef<'iframe'>, BoxOwnProps> {
@@ -47,14 +44,13 @@ export const Embed: ForwardRef<HTMLIFrameElement, EmbedProps> =
frameBorder,
allowFullScreen,
allow,
...getIframeProps(rest),
...rest,
}

return (
<Box
variant={variant}
sx={sx}
{...getContainerProps(rest)}
{...__internalProps({
__css: {
width: '100%',
10 changes: 6 additions & 4 deletions packages/components/src/Field.tsx
Original file line number Diff line number Diff line change
@@ -3,9 +3,9 @@ import React from 'react'
import { Box } from './Box'
import { Label } from './Label'
import { Input, InputProps } from './Input'
import { getMargin, MarginProps, omitMargin } from './util'
import type { ThemeUICSSObject } from '@theme-ui/css'

export interface FieldOwnProps extends MarginProps {
export interface FieldOwnProps {
/**
* Text for Label component
*/
@@ -14,6 +14,7 @@ export interface FieldOwnProps extends MarginProps {
* Used for the for, id, and name attributes
*/
name?: string
containerSx?: ThemeUICSSObject
}

export type FieldProps<T extends React.ElementType> = FieldOwnProps &
@@ -39,6 +40,7 @@ export const Field = React.forwardRef(function Field<
label,
id,
name,
containerSx,
...rest
}: FieldProps<T>,
ref: React.ForwardedRef<unknown>
@@ -49,11 +51,11 @@ export const Field = React.forwardRef(function Field<
ref,
name,
id: fieldIdentifier,
...omitMargin(rest),
...rest,
} as React.ComponentPropsWithRef<T>

return (
<Box {...getMargin(rest)}>
<Box sx={containerSx}>
<Label htmlFor={fieldIdentifier}>{label}</Label>
<Control {...controlProps} />
</Box>
4 changes: 2 additions & 2 deletions packages/components/src/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react'
import { ThemeUICSSObject } from '@theme-ui/css'
import type { ThemeUICSSObject } from '@theme-ui/css'

import { Box, BoxProps } from './Box'
import { ForwardRef } from './types'
import type { ForwardRef } from './types'
import { __internalProps } from './util'

export type MessageProps = BoxProps
2 changes: 1 addition & 1 deletion packages/components/src/Progress.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'

import { ThemeUICSSObject } from '@theme-ui/css'
import type { ThemeUICSSObject } from '@theme-ui/css'

import { Box, BoxOwnProps, BoxProps } from './Box'
import type { Assign, ForwardRef } from './types'
11 changes: 6 additions & 5 deletions packages/components/src/Select.tsx
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@ import { get, ThemeUICSSObject } from '@theme-ui/css'

import { Box, BoxOwnProps, BoxProps } from './Box'
import { SVG, SVGProps } from './SVG'
import { getMargin, omitMargin, __internalProps } from './util'
import { Assign, ForwardRef } from './types'
import { __internalProps } from './util'
import type { Assign, ForwardRef } from './types'

const DownArrow = (props: SVGProps) => (
<SVG {...props}>
@@ -16,6 +16,7 @@ const DownArrow = (props: SVGProps) => (
export interface SelectProps
extends Assign<React.ComponentPropsWithRef<'select'>, BoxOwnProps> {
arrow?: React.ReactElement
containerSx?: ThemeUICSSObject
}

/**
@@ -24,7 +25,7 @@ export interface SelectProps
* @see https://theme-ui.com/components/select/
*/
export const Select: ForwardRef<HTMLSelectElement, SelectProps> =
React.forwardRef(function Select({ arrow, ...props }, ref) {
React.forwardRef(function Select({ arrow, containerSx, ...props }, ref) {
const __css: ThemeUICSSObject = {
display: 'block',
width: '100%',
@@ -41,16 +42,16 @@ export const Select: ForwardRef<HTMLSelectElement, SelectProps> =

return (
<Box
{...getMargin(props)}
sx={{
display: 'flex',
...containerSx,
}}
>
<Box
ref={ref}
as="select"
variant="select"
{...(omitMargin(props) as BoxProps)}
{...(props as BoxProps)}
{...__internalProps({ __themeKey: 'forms', __css })}
/>
{arrow || (
5 changes: 3 additions & 2 deletions packages/components/src/Switch.tsx
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ const SIZE = 18
export interface SwitchProps
extends Assign<React.ComponentPropsWithRef<'input'>, BoxOwnProps> {
label?: string
containerSx?: ThemeUICSSObject
}

/**
@@ -23,7 +24,7 @@ export interface SwitchProps
*/
export const Switch: ForwardRef<HTMLInputElement, SwitchProps> =
React.forwardRef(function Switch(
{ className, label, sx, variant = 'switch', ...rest },
{ className, label, sx, containerSx, variant = 'switch', ...rest },
ref
) {
const __css: ThemeUICSSObject = {
@@ -59,7 +60,7 @@ export const Switch: ForwardRef<HTMLInputElement, SwitchProps> =
}

return (
<Label sx={{ cursor: 'pointer' }}>
<Label sx={{ cursor: 'pointer', ...containerSx }}>
<Box
ref={ref}
as="input"
23 changes: 0 additions & 23 deletions packages/components/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,5 @@
import type { ThemeUICSSObject, ThemeUICSSProperties } from '@theme-ui/css'

export const getProps =
(test: (k: string) => boolean) =>
<T extends object>(props: T): T => {
const next: Partial<T> = {}
for (const key in props) {
if (test(key || '')) next[key] = props[key]
}
return next as T
}

const MRE = /^m[trblxy]?$/

export interface MarginProps
extends Pick<
ThemeUICSSProperties,
'm' | 'mt' | 'mr' | 'mb' | 'ml' | 'mx' | 'my'
> {}

export const getMargin: (props: MarginProps) => MarginProps = getProps((k) =>
MRE.test(k)
)
export const omitMargin = getProps((k) => !MRE.test(k))

/** @internal */
export function __internalProps(props: __ThemeUIComponentsInternalProps) {
return props as {}
25 changes: 2 additions & 23 deletions packages/components/test/Box.spec.tsx
Original file line number Diff line number Diff line change
@@ -13,30 +13,10 @@ import { __internalProps } from '../src/util'

describe('Box', () => {
test('renders', () => {
const json = renderJSON(<Box p={2}>Hello</Box>)
const json = renderJSON(<Box>Hello</Box>)
expect(json).toMatchSnapshot()
})

test('renders with padding props', () => {
const json = renderJSON(<Box px={2} py={4} />)
expect(json).toHaveStyleRule('padding-left', '8px')
expect(json).toHaveStyleRule('padding-right', '8px')
expect(json).toHaveStyleRule('padding-top', '32px')
expect(json).toHaveStyleRule('padding-bottom', '32px')
})

test('renders with margin props', () => {
const json = renderJSON(<Box m={3} mb={4} />)
expect(json).toHaveStyleRule('margin', '16px')
expect(json).toHaveStyleRule('margin-bottom', '32px')
})

test('renders with color props', () => {
const json = renderJSON(<Box color="tomato" bg="black" />)
expect(json).toHaveStyleRule('color', 'tomato')
expect(json).toHaveStyleRule('background-color', 'black')
})

test('renders with sx prop', () => {
const json = renderJSON(
<Box
@@ -63,8 +43,7 @@ describe('Box', () => {
test('renders with base styles', () => {
const json = renderJSON(
<Box
bg="cyan"
sx={{ color: 'tomato' }}
sx={{ bg: 'cyan', color: 'tomato' }}
{...__internalProps({
__css: {
p: 4,
Loading