Skip to content

Commit 2f27f7b

Browse files
committed
fix(core): filter array scales from Theme type given in .sx
1 parent 494c345 commit 2f27f7b

File tree

12 files changed

+1440
-559
lines changed

12 files changed

+1440
-559
lines changed

packages/components/src/Box.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
ArrayInterpolation,
33
CSSObject,
44
Interpolation,
5-
jsx,
65
useTheme,
76
} from '@emotion/react'
87
import React, { forwardRef } from 'react'
@@ -12,6 +11,7 @@ import {
1211
ThemeUICSSProperties,
1312
ThemeUIStyleObject,
1413
} from '@theme-ui/css'
14+
import { Theme } from '@theme-ui/core'
1515
import type { Assign, ForwardRef } from './types'
1616
import type { __ThemeUIComponentsInternalProps } from './util'
1717

@@ -59,7 +59,7 @@ export interface BoxOwnProps extends BoxSystemProps {
5959
as?: React.ElementType
6060
variant?: string
6161
css?: Interpolation<any>
62-
sx?: ThemeUIStyleObject
62+
sx?: ThemeUIStyleObject<Theme>
6363
}
6464

6565
export interface BoxProps

packages/core/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export type {
2323
Scale,
2424
StylePropertyValue,
2525
TLengthStyledSystem,
26-
Theme,
2726
ThemeDerivedStyles,
2827
ThemeStyles,
2928
ThemeUICSSObject,

packages/core/src/types.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
import { Interpolation } from '@emotion/react'
2-
import { ThemeUIStyleObject, Theme as ThemeUITheme } from '@theme-ui/css'
2+
import {
3+
Scale,
4+
ScaleDict,
5+
ThemeUIStyleObject,
6+
Theme as ThemeUITheme,
7+
} from '@theme-ui/css'
8+
9+
export interface UserThemes {}
10+
11+
/** @internal */
12+
export type _UserTheme = UserThemes[keyof UserThemes]
13+
14+
/** Theme without array scales, so it's easier to read from inside of .sx prop */
15+
export type WidenedTheme = {
16+
[P in keyof ThemeUITheme]: ThemeUITheme[P] extends Scale<infer R> | undefined
17+
? ScaleDict<R>
18+
: ThemeUITheme[P]
19+
} & ThemeUITheme
20+
21+
export type Theme<TTheme = {}> = _UserTheme extends never
22+
? ThemeUITheme<TTheme>
23+
: _UserTheme
24+
25+
/** @internal */
26+
export type _JSXTheme = _UserTheme extends never ? WidenedTheme : _UserTheme
327

428
export interface SxProp {
529
/**
@@ -8,7 +32,7 @@ export interface SxProp {
832
*
933
* @see https://theme-ui.com/sx-prop/
1034
*/
11-
sx?: ThemeUIStyleObject
35+
sx?: ThemeUIStyleObject<_JSXTheme>
1236
/**
1337
* Theme UI uses Emotion's JSX function. You can pass styles to it directly
1438
* using `css` prop.

packages/core/test/react-jsx.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
*/
55
/* eslint-disable no-lone-blocks */
66
import { renderJSON, NotHas, Assert, expecter } from '@theme-ui/test-utils'
7+
import { matchers } from '@emotion/jest'
78

8-
import { SxProp, ThemeUIJSX } from '../src'
9+
import { SxProp, ThemeProvider, ThemeUIJSX } from '../src'
10+
11+
expect.extend(matchers)
912

1013
describe('JSX', () => {
1114
test('accepts sx prop', () => {
@@ -30,6 +33,24 @@ describe('JSX', () => {
3033
).toMatchSnapshot()
3134
})
3235

36+
test('sx prop gives a theme that can be read as array or object', () => {
37+
const json = renderJSON(
38+
<ThemeProvider
39+
theme={{ shadows: { small: '0 0 4px rgba(0, 0, 0, .125)' } }}
40+
>
41+
<div
42+
sx={(t) => ({
43+
boxShadow: t.shadows?.small,
44+
'&:hover': {
45+
boxShadow: t.shadows?.[2],
46+
},
47+
})}
48+
/>
49+
</ThemeProvider>
50+
)
51+
expect(json).toHaveStyleRule('box-shadow', '0 0 4px rgba(0, 0, 0, .125)')
52+
})
53+
3354
test('accepts css prop', () => {
3455
const expectSnippet = expecter(
3556
`/** @jsxImportSource ./packages/core */

packages/css/src/index.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,12 @@ const transforms = [
304304
)
305305

306306
const responsive =
307-
(styles: Exclude<ThemeUIStyleObject, ThemeDerivedStyles>) =>
307+
(styles: Exclude<ThemeUIStyleObject<Theme>, ThemeDerivedStyles<Theme>>) =>
308308
(theme?: Theme) => {
309-
const next: Exclude<ThemeUIStyleObject, ThemeDerivedStyles> = {}
309+
const next: Exclude<
310+
ThemeUIStyleObject<Theme>,
311+
ThemeDerivedStyles<Theme>
312+
> = {}
310313
const breakpoints =
311314
(theme && (theme.breakpoints as string[])) || defaultBreakpoints
312315
const mediaQueries = [
@@ -347,12 +350,12 @@ const responsive =
347350
type CssPropsArgument = { theme: Theme } | Theme
348351

349352
export const css =
350-
(args: ThemeUIStyleObject = {}) =>
353+
<TTheme extends Theme<{}>>(args: ThemeUIStyleObject<TTheme> = {}) =>
351354
(props: CssPropsArgument = {}): CSSObject => {
352-
const theme: Theme = {
355+
const theme = {
353356
...defaultTheme,
354357
...('theme' in props ? props.theme : props),
355-
}
358+
} as TTheme
356359
// insert variant props before responsive styles, so they can be merged
357360
// we need to maintain order of the style props, so if a variant is place in the middle
358361
// of other props, it will extends its props at that same location order.

packages/css/src/types.ts

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ export interface CSSProperties
3232
/**
3333
* Map of all CSS pseudo selectors (`:hover`, `:focus`, ...)
3434
*/
35-
export type CSSPseudoSelectorProps = { [K in CSS.Pseudos]?: ThemeUIStyleObject }
35+
export type CSSPseudoSelectorProps<TTheme> = {
36+
[K in CSS.Pseudos]?: ThemeUIStyleObject<TTheme>
37+
}
3638

3739
interface AliasesCSSProperties {
3840
/**
@@ -437,10 +439,10 @@ export interface ThemeUIExtendedCSSProperties
437439

438440
type ThemeUIStyleValue<T> = ResponsiveStyleValue<T | ObjectWithDefault<T> | T[]>
439441

440-
export type StylePropertyValue<T> =
442+
export type StylePropertyValue<T, TTheme extends Theme = Theme> =
441443
| ThemeUIStyleValue<Exclude<T, undefined>>
442-
| ((theme: Theme) => ThemeUIStyleValue<Exclude<T, undefined>> | undefined)
443-
| ThemeUIStyleObject
444+
| ((theme: TTheme) => ThemeUIStyleValue<Exclude<T, undefined>> | undefined)
445+
| ThemeUIStyleObject<TTheme>
444446
| ThemeUIEmpty
445447

446448
export type ThemeUICSSProperties = {
@@ -474,8 +476,8 @@ export interface VariantProperty {
474476
variant?: string
475477
}
476478

477-
export interface ThemeDerivedStyles {
478-
(theme: Theme): ThemeUICSSObject
479+
export interface ThemeDerivedStyles<TTheme = Theme> {
480+
(theme: TTheme): ThemeUICSSObject
479481
}
480482

481483
export interface Label {
@@ -497,7 +499,7 @@ export interface CSSOthersObject {
497499

498500
export interface ThemeUICSSObject
499501
extends ThemeUICSSProperties,
500-
CSSPseudoSelectorProps,
502+
CSSPseudoSelectorProps<Theme>,
501503
CSSOthersObject,
502504
VariantProperty,
503505
Label {}
@@ -507,7 +509,9 @@ export interface ThemeUICSSObject
507509
* such that properties that are part of the `Theme` will be transformed to
508510
* their corresponding values. Other valid CSS properties are also allowed.
509511
*/
510-
export type ThemeUIStyleObject = ThemeUICSSObject | ThemeDerivedStyles
512+
export type ThemeUIStyleObject<TTheme = Theme> =
513+
| ThemeUICSSObject
514+
| ThemeDerivedStyles<TTheme>
511515

512516
export type TLengthStyledSystem = string | 0 | number
513517

@@ -565,7 +569,7 @@ export interface ColorMode extends ScaleDict<CSS.Property.Color> {
565569
text?: ColorOrNestedColorScale
566570

567571
/**
568-
* Primary brand color for links, buttons, etc.
572+
* Primary brand coloThr for links, buttons, etc.
569573
*/
570574
primary?: ColorOrNestedColorScale
571575

@@ -601,40 +605,40 @@ export type ColorModesScale = ColorMode & {
601605
}
602606
}
603607

604-
export interface ThemeStyles {
605-
tr?: ThemeUIStyleObject
606-
th?: ThemeUIStyleObject
607-
td?: ThemeUIStyleObject
608-
em?: ThemeUIStyleObject
609-
strong?: ThemeUIStyleObject
610-
div?: ThemeUIStyleObject
611-
p?: ThemeUIStyleObject
612-
b?: ThemeUIStyleObject
613-
i?: ThemeUIStyleObject
614-
a?: ThemeUIStyleObject
615-
h1?: ThemeUIStyleObject
616-
h2?: ThemeUIStyleObject
617-
h3?: ThemeUIStyleObject
618-
h4?: ThemeUIStyleObject
619-
h5?: ThemeUIStyleObject
620-
h6?: ThemeUIStyleObject
621-
img?: ThemeUIStyleObject
622-
pre?: ThemeUIStyleObject
623-
code?: ThemeUIStyleObject
624-
ol?: ThemeUIStyleObject
625-
ul?: ThemeUIStyleObject
626-
li?: ThemeUIStyleObject
627-
blockquote?: ThemeUIStyleObject
628-
hr?: ThemeUIStyleObject
629-
table?: ThemeUIStyleObject
630-
delete?: ThemeUIStyleObject
631-
inlineCode?: ThemeUIStyleObject
632-
thematicBreak?: ThemeUIStyleObject
633-
root?: ThemeUIStyleObject
634-
[key: string]: ThemeUIStyleObject | undefined
608+
export interface ThemeStyles<TTheme = Theme> {
609+
tr?: ThemeUIStyleObject<TTheme>
610+
th?: ThemeUIStyleObject<TTheme>
611+
td?: ThemeUIStyleObject<TTheme>
612+
em?: ThemeUIStyleObject<TTheme>
613+
strong?: ThemeUIStyleObject<TTheme>
614+
div?: ThemeUIStyleObject<TTheme>
615+
p?: ThemeUIStyleObject<TTheme>
616+
b?: ThemeUIStyleObject<TTheme>
617+
i?: ThemeUIStyleObject<TTheme>
618+
a?: ThemeUIStyleObject<TTheme>
619+
h1?: ThemeUIStyleObject<TTheme>
620+
h2?: ThemeUIStyleObject<TTheme>
621+
h3?: ThemeUIStyleObject<TTheme>
622+
h4?: ThemeUIStyleObject<TTheme>
623+
h5?: ThemeUIStyleObject<TTheme>
624+
h6?: ThemeUIStyleObject<TTheme>
625+
img?: ThemeUIStyleObject<TTheme>
626+
pre?: ThemeUIStyleObject<TTheme>
627+
code?: ThemeUIStyleObject<TTheme>
628+
ol?: ThemeUIStyleObject<TTheme>
629+
ul?: ThemeUIStyleObject<TTheme>
630+
li?: ThemeUIStyleObject<TTheme>
631+
blockquote?: ThemeUIStyleObject<TTheme>
632+
hr?: ThemeUIStyleObject<TTheme>
633+
table?: ThemeUIStyleObject<TTheme>
634+
delete?: ThemeUIStyleObject<TTheme>
635+
inlineCode?: ThemeUIStyleObject<TTheme>
636+
thematicBreak?: ThemeUIStyleObject<TTheme>
637+
root?: ThemeUIStyleObject<TTheme>
638+
[key: string]: ThemeUIStyleObject<TTheme> | undefined
635639
}
636640

637-
export interface Theme {
641+
export interface Theme<TTheme = {}> {
638642
breakpoints?: Array<string>
639643
mediaQueries?: { [size: string]: string }
640644
space?: Scale<CSS.Property.Margin<number | string>>
@@ -742,7 +746,7 @@ export interface Theme {
742746
* @see https://theme-ui.com/components/variants
743747
* @see https://theme-ui.com/components/grid#variants
744748
*/
745-
grids?: Record<string, ThemeUIStyleObject>
749+
grids?: Record<string, ThemeUIStyleObject<TTheme>>
746750

747751
/**
748752
* Button variants can be defined in the `theme.buttons` object. The `Button`
@@ -752,7 +756,7 @@ export interface Theme {
752756
* @see https://theme-ui.com/components/variants
753757
* @see https://theme-ui.com/components/button#variants
754758
*/
755-
buttons?: Record<string, ThemeUIStyleObject>
759+
buttons?: Record<string, ThemeUIStyleObject<TTheme>>
756760

757761
/**
758762
* Text style variants can be defined in the `theme.text` object. The `Text`
@@ -762,7 +766,7 @@ export interface Theme {
762766
* @see https://theme-ui.com/components/variants
763767
* @see https://theme-ui.com/components/text#variants
764768
*/
765-
text?: Record<string, ThemeUIStyleObject>
769+
text?: Record<string, ThemeUIStyleObject<TTheme>>
766770

767771
/**
768772
* Link variants can be defined in the `theme.links` object. By default the
@@ -772,7 +776,7 @@ export interface Theme {
772776
* @see https://theme-ui.com/components/variants
773777
* @see https://theme-ui.com/components/link#variants
774778
*/
775-
links?: Record<string, ThemeUIStyleObject>
779+
links?: Record<string, ThemeUIStyleObject<TTheme>>
776780

777781
/**
778782
* Image style variants can be defined in the `theme.images` object.
@@ -781,7 +785,7 @@ export interface Theme {
781785
* @see https://theme-ui.com/components/variants
782786
* @see https://theme-ui.com/components/image#variants
783787
*/
784-
images?: Record<string, ThemeUIStyleObject>
788+
images?: Record<string, ThemeUIStyleObject<TTheme>>
785789

786790
/**
787791
* Card style variants can be defined in `the theme.cards` object. By default
@@ -791,7 +795,7 @@ export interface Theme {
791795
* @see https://theme-ui.com/components/variants
792796
* @see https://theme-ui.com/components/card#variants
793797
*/
794-
cards?: Record<string, ThemeUIStyleObject>
798+
cards?: Record<string, ThemeUIStyleObject<TTheme>>
795799

796800
/**
797801
* Container variants can be defined in the `theme.layout` object. The
@@ -802,7 +806,7 @@ export interface Theme {
802806
* @see https://theme-ui.com/components/variants
803807
* @see https://theme-ui.com/components/container#variants
804808
*/
805-
layout?: Record<string, ThemeUIStyleObject>
809+
layout?: Record<string, ThemeUIStyleObject<TTheme>>
806810

807811
/**
808812
* Label variants can be defined in `theme.forms` and the component uses the
@@ -836,7 +840,7 @@ export interface Theme {
836840
* @see https://theme-ui.com/components/checkbox#variants
837841
* @see https://theme-ui.com/components/slider#variants
838842
*/
839-
forms?: Record<string, ThemeUIStyleObject>
843+
forms?: Record<string, ThemeUIStyleObject<TTheme>>
840844

841845
/**
842846
* Badge variants can be defined in `theme.badges`. The `Badge` component uses
@@ -846,7 +850,7 @@ export interface Theme {
846850
* @see https://theme-ui.com/components/variants
847851
* @see https://theme-ui.com/components/badge#variants
848852
*/
849-
badges?: Record<string, ThemeUIStyleObject>
853+
badges?: Record<string, ThemeUIStyleObject<TTheme>>
850854

851855
/**
852856
* Alert variants can be defined in `theme.alerts`. The `Alert` component uses
@@ -856,7 +860,7 @@ export interface Theme {
856860
* @see https://theme-ui.com/components/variants
857861
* @see https://theme-ui.com/components/alert#variants
858862
*/
859-
alerts?: Record<string, ThemeUIStyleObject>
863+
alerts?: Record<string, ThemeUIStyleObject<TTheme>>
860864

861865
/**
862866
* Message variants can be defined in the `theme.messages` object.
@@ -865,5 +869,5 @@ export interface Theme {
865869
* @see https://theme-ui.com/components/variants
866870
* @see https://theme-ui.com/components/message#variants
867871
*/
868-
messages?: Record<string, ThemeUIStyleObject>
872+
messages?: Record<string, ThemeUIStyleObject<TTheme>>
869873
}

packages/css/test/errors-and-inference.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('Theme', () => {
5959
return get(t, 'sizes.5')
6060
}
6161
})
62-
`).toInfer('theme', 'Theme')
62+
`).toInfer('theme', 'Theme<{}>')
6363
})
6464

6565
test('accepts additional properties by declaration merging', () => {

packages/css/test/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe(makeTheme, () => {
2626
expecter('import { makeTheme } from "./packages/css/utils"', {
2727
jsx: false,
2828
})('const t = makeTheme("banana")').toFail(
29-
/Type '"banana"' has no properties in common with type 'Theme'./
29+
/Type '"banana"' has no properties in common with type 'Theme<{}>'./
3030
)
3131
})
3232
})

0 commit comments

Comments
 (0)