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

Migrate @emotion/native to TypeScript #2861

Closed
wants to merge 13 commits into from
Closed
5 changes: 5 additions & 0 deletions .changeset/gentle-fishes-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/native': patch
---

Added a missing overload to the TypeScript types for the `css` function so that functions are allowed as arguments when MergedProps is present.
5 changes: 5 additions & 0 deletions .changeset/good-scissors-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/native': minor
---

The source code has been migrated to TypeScript so the TypeScript declarations are now generated from the source.
5 changes: 5 additions & 0 deletions .changeset/smart-kids-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/primitives': minor
---

Source code has been migrated to TypeScript so from now on type declarations will be available in the published package.
5 changes: 5 additions & 0 deletions .changeset/strange-squids-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/native': minor
---

Remove `styled.ToolbarAndroid` since the `ToolbarAndroid` component was removed from React Native in version 0.61. This version of React Native was published in 2019.
6 changes: 6 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,11 @@ jobs:
- uses: actions/checkout@v3
- uses: ./.github/actions/ci-setup

# dtslint can produce different results on the compiled .d.ts files and
# the stub .d.ts generated by `preconstruct dev`. We build here because we
# want to lint the compiled .d.ts files.
- name: Preconstruct
run: yarn build

- name: dtslint
run: yarn test:typescript
6 changes: 2 additions & 4 deletions packages/native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,15 @@
"files": [
"src",
"dist",
"types/*.d.ts",
"macro.js"
],
"types": "types/index.d.ts",
"types": "dist/emotion-native.cjs.d.ts",
"devDependencies": {
"@babel/core": "^7.18.5",
"@definitelytyped/dtslint": "0.0.112",
"@types/react-native": "^0.63.2",
"react": "16.14.0",
"react-native": "^0.63.2",
"typescript": "^4.5.5"
"react-native": "^0.63.2"
},
"dependencies": {
"@emotion/primitives-core": "^11.10.0"
Expand Down
9 changes: 0 additions & 9 deletions packages/native/src/base.js

This file was deleted.

11 changes: 8 additions & 3 deletions packages/native/types/base.d.ts → packages/native/src/base.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Definitions by: Pat Sissons <https://github.com/patsissons>
// TypeScript Version: 3.4

import { StyleSheet } from 'react-native'
import { createStyled } from '@emotion/primitives-core'
import { Theme } from '@emotion/react'
import * as RN from 'react-native'

type ReactNative = typeof RN

export type ReactNativeStyle = RN.ViewStyle | RN.TextStyle | RN.ImageStyle

export type ReactNativeStyleType<Props> = Props extends {
Expand Down Expand Up @@ -202,4 +202,9 @@ export interface CreateStyled {
>
}

export const styled: CreateStyled
/**
* a function that returns a styled component which render styles in React Native
*/
let styled = createStyled(StyleSheet)

export { styled }
2 changes: 0 additions & 2 deletions packages/native/src/index.d.ts

This file was deleted.

61 changes: 0 additions & 61 deletions packages/native/src/index.js

This file was deleted.

171 changes: 171 additions & 0 deletions packages/native/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import * as ReactNative from 'react-native'
import { createCss } from '@emotion/primitives-core'

import {
CreateStyled as BaseCreateStyled,
CreateStyledComponent,
CSSInterpolation,
Interpolation,
ReactNativeStyle,
ReactNativeStyleType,
styled as baseStyled
} from './base'
import React from 'react'
import { Theme } from '@emotion/react'

export type {
ArrayCSSInterpolation,
ArrayInterpolation,
CreateStyledComponent,
CSSInterpolation,
FilteringStyledOptions,
FunctionInterpolation,
Interpolation,
InterpolationPrimitive,
ObjectInterpolation,
ReactNativeStyle,
StyledComponent,
StyledOptions
} from './base'

export interface EmotionNativeCss {
<StyleType extends ReactNativeStyle = ReactNativeStyle>(
template: TemplateStringsArray,
...args: Array<Interpolation>
): StyleType

<StyleType extends ReactNativeStyle = ReactNativeStyle>(
...args: Array<StyleType>
): StyleType

<StyleType extends ReactNativeStyle = ReactNativeStyle>(
...args: Array<CSSInterpolation>
): StyleType

<MergedProps, StyleType extends ReactNativeStyle = ReactNativeStyle>(
this: MergedProps,
...args: Array<Interpolation<MergedProps, StyleType>>
): StyleType
}

export const css: EmotionNativeCss = createCss(ReactNative.StyleSheet)

// those 2 are just copies of the `BaseCreateStyled` with supplied `C` type argument
type HostClassComponent<C extends React.ComponentClass<any>> =
CreateStyledComponent<
React.ComponentProps<C> & { theme?: Theme; as?: React.ElementType },
{},
{ ref?: React.Ref<InstanceType<C>> },
ReactNativeStyleType<React.ComponentProps<C>>
>
type HostFunctionComponent<C extends React.FunctionComponent<any>> =
CreateStyledComponent<
React.ComponentProps<C> & { theme?: Theme; as?: React.ElementType },
{},
{},
ReactNativeStyleType<React.ComponentProps<C>>
>

type RN = typeof ReactNative

const components = [
'ActivityIndicator',
'DatePickerIOS',
'DrawerLayoutAndroid',
'FlatList',
'Image',
'ImageBackground',
'KeyboardAvoidingView',
'ListView',
'Modal',
'NavigatorIOS',
'Picker',
'PickerIOS',
'ProgressBarAndroid',
'ProgressViewIOS',
'RecyclerViewBackedScrollView',
'RefreshControl',
'SafeAreaView',
'ScrollView',
'SectionList',
'SegmentedControlIOS',
'Slider',
'SnapshotViewIOS',
'StatusBar',
'SwipeableListView',
'Switch',
'SwitchIOS',
'TabBarIOS',
'Text',
'TextInput',
'TouchableHighlight',
'TouchableNativeFeedback',
'TouchableOpacity',
'TouchableWithoutFeedback',
'View',
'ViewPagerAndroid',
'Pressable'
]

type RNClassComponent =
| 'ActivityIndicator'
| 'Button'
| 'DatePickerIOS'
| 'DrawerLayoutAndroid'
| 'FlatList'
| 'Image'
| 'ImageBackground'
| 'KeyboardAvoidingView'
| 'ListView'
| 'Modal'
| 'NavigatorIOS'
| 'Picker'
| 'PickerIOS'
| 'ProgressBarAndroid'
| 'ProgressViewIOS'
| 'RecyclerViewBackedScrollView'
| 'RefreshControl'
| 'SafeAreaView'
| 'ScrollView'
| 'SectionList'
| 'SegmentedControlIOS'
| 'Slider'
| 'SnapshotViewIOS'
| 'StatusBar'
| 'SwipeableListView'
| 'Switch'
| 'SwitchIOS'
| 'TabBarIOS'
| 'Text'
| 'TextInput'
| 'TouchableHighlight'
| 'TouchableNativeFeedback'
| 'TouchableOpacity'
| 'TouchableWithoutFeedback'
| 'View'
| 'ViewPagerAndroid'

type RNFunctionComponent = 'Pressable'

export type StyledComponents = {
[C in RNClassComponent]: HostClassComponent<RN[C]>
} &
{ [C in RNFunctionComponent]: HostFunctionComponent<RN[C]> }

export type CreateStyled = BaseCreateStyled & StyledComponents

const styled: CreateStyled = components.reduce(
(acc, comp) =>
Object.defineProperty(acc, comp, {
enumerable: true,
configurable: false,
get() {
return baseStyled(
ReactNative[comp as RNClassComponent | RNFunctionComponent]
)
}
}),
baseStyled
) as any

export default styled
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { css } from '@emotion/native'
import { StyleSheet } from 'react-native'
import { Interpolation, ReactNativeStyle } from '../src/base'

jest.mock('react-native')

let returnArguments = (...args) => args
let returnArguments = <A extends unknown[]>(...args: A): A => args

describe('Emotion native css', () => {
test('basic', () => {
Expand Down Expand Up @@ -83,8 +84,12 @@ describe('Emotion native css', () => {
it('allows function interpolations when this.mergedProps is defined', () => {
expect(
StyleSheet.flatten(
css.call({ thing: true }, props => ({
color: props.thing && 'hotpink'
css.call<
{ thing: boolean },
Interpolation<{ thing: boolean }, ReactNativeStyle>[],
ReactNativeStyle
>({ thing: true }, (props: { thing: boolean }) => ({
color: props.thing ? 'hotpink' : undefined
}))
)
).toEqual({ color: 'hotpink' })
Expand All @@ -93,8 +98,12 @@ describe('Emotion native css', () => {
it('works with nested functions', () => {
expect(
StyleSheet.flatten(
css.call({ thing: true }, props => () => ({
color: props.thing && 'hotpink'
css.call<
{ thing: boolean },
Interpolation<{ thing: boolean }, ReactNativeStyle>[],
ReactNativeStyle
>({ thing: true }, props => () => ({
color: props.thing ? 'hotpink' : undefined
}))
)
).toEqual({ color: 'hotpink' })
Expand All @@ -103,7 +112,11 @@ describe('Emotion native css', () => {
it('works with functions in tagged template literals', () => {
expect(
StyleSheet.flatten(
css.call(
css.call<
unknown,
[TemplateStringsArray, () => string],
ReactNativeStyle
>(
{},
...returnArguments`
color: ${() => 'hotpink'};
Expand Down
Loading