Skip to content
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

components: Remove style props #2326

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
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
74 changes: 3 additions & 71 deletions packages/components/src/Box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Up @@ -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) => (
Expand Down Expand Up @@ -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
Expand All @@ -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"
Expand Down
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> {
Expand Down Expand Up @@ -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%',
Expand Down
10 changes: 6 additions & 4 deletions packages/components/src/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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 &
Expand All @@ -39,6 +40,7 @@ export const Field = React.forwardRef(function Field<
label,
id,
name,
containerSx,
...rest
}: FieldProps<T>,
ref: React.ForwardedRef<unknown>
Expand All @@ -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>
Expand Down
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
Expand Down
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'
Expand Down
11 changes: 6 additions & 5 deletions packages/components/src/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}>
Expand All @@ -16,6 +16,7 @@ const DownArrow = (props: SVGProps) => (
export interface SelectProps
extends Assign<React.ComponentPropsWithRef<'select'>, BoxOwnProps> {
arrow?: React.ReactElement
containerSx?: ThemeUICSSObject
}

/**
Expand All @@ -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%',
Expand All @@ -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 || (
Expand Down
5 changes: 3 additions & 2 deletions packages/components/src/Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const SIZE = 18
export interface SwitchProps
extends Assign<React.ComponentPropsWithRef<'input'>, BoxOwnProps> {
label?: string
containerSx?: ThemeUICSSObject
}

/**
Expand All @@ -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 = {
Expand Down Expand Up @@ -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"
Expand Down
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 {}
Expand Down
25 changes: 2 additions & 23 deletions packages/components/test/Box.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
Loading
Loading