From 107ecde00f1e77d9b4a0e1a6692ce02108f2e4b7 Mon Sep 17 00:00:00 2001 From: Jeremy Hamilton Date: Tue, 2 Mar 2021 16:01:51 -0800 Subject: [PATCH] fix withTheme typing --- package.json | 3 +- src/avatar/Accessory.tsx | 1 + src/avatar/Avatar.tsx | 196 +++++++++--------- .../__tests__/__snapshots__/Avatar.js.snap | 28 +-- src/buttons/Button.tsx | 1 + src/card/Card.tsx | 105 +++++----- src/card/CardFeaturedSubtitle.tsx | 1 + src/card/CardFeaturedTitle.tsx | 1 + src/card/CardTitle.tsx | 1 + src/checkbox/CheckBox.tsx | 1 + src/config/__tests__/withTheme.js | 8 +- src/config/withTheme.tsx | 28 +-- src/header/Header.tsx | 1 + src/image/Image.tsx | 1 + src/input/Input.tsx | 1 + src/list/ListItem.tsx | 137 ++++++------ .../__tests__/__snapshots__/ListItem.js.snap | 24 +-- src/pricing/PricingCard.tsx | 1 + .../__snapshots__/PricingCard.js.snap | 8 +- src/searchbar/SearchBar-android.tsx | 1 + src/searchbar/SearchBar-default.tsx | 1 + src/searchbar/SearchBar-ios.tsx | 1 + src/searchbar/SearchBar.tsx | 1 + .../__snapshots__/SearchBar-android.js.snap | 26 +-- .../__snapshots__/SearchBar-default.js.snap | 18 +- .../__snapshots__/SearchBar-ios.js.snap | 26 +-- src/social/SocialIcon.tsx | 2 + src/tile/FeaturedTile.tsx | 5 +- src/tile/Tile.tsx | 7 +- src/tile/__tests__/__snapshots__/Tile.js.snap | 42 +--- src/tooltip/Tooltip.tsx | 1 + tsconfig.json | 4 +- yarn.lock | 5 + 33 files changed, 336 insertions(+), 351 deletions(-) diff --git a/package.json b/package.json index c020a6127d..d6fafa3d0c 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,8 @@ "react-native-vector-icons": "^7.0.0", "react-test-renderer": "^16.13.1", "rimraf": "^3.0.2", - "typescript": "^4.1.3" + "typescript": "^4.1.3", + "utility-types": "^3.10.0" }, "peerDependencies": { "react-native-safe-area-context": "^3.1.9", diff --git a/src/avatar/Accessory.tsx b/src/avatar/Accessory.tsx index f64cfe8adf..e8dcc912ba 100644 --- a/src/avatar/Accessory.tsx +++ b/src/avatar/Accessory.tsx @@ -44,6 +44,7 @@ const Accessory: React.FunctionComponent = ({ > {source ? ( + //@ts-ignore { - Accessory: typeof Accessory; -} +interface Avatar extends React.FunctionComponent {} -const AvatarComponent: Avatar = Object.assign( - ({ - onPress, - onLongPress, - Component = onPress || onLongPress ? TouchableOpacity : View, - containerStyle, - icon, - iconStyle, - source, - size = 'small', - avatarStyle, - rounded, - title, - titleStyle, - overlayContainerStyle, - imageProps, - placeholderStyle, - renderPlaceholderContent, - ImageComponent = RNImage, - children, - ...attributes - }: React.PropsWithChildren) => { - const width = - typeof size === 'number' ? size : avatarSizes[size] || avatarSizes.small; - const height = width; - const titleSize = width / 2; - const iconSize = width / 2; - const PlaceholderContent = - (renderPlaceholderContent && - renderNode(undefined, renderPlaceholderContent)) || - (title && ( - - {title} - - )) || - (icon && ( - - )); - // @ts-ignore - const hidePlaceholder = !(source && source.uri); - // Merge image container style - const imageContainerStyle = StyleSheet.flatten([ - styles.overlayContainer, - rounded && { borderRadius: width / 2, overflow: 'hidden' }, - overlayContainerStyle, - imageProps && imageProps.containerStyle, - ]); - if (imageProps && imageProps.containerStyle) { - delete imageProps.containerStyle; - } - return ( - ) => { + const width = + typeof size === 'number' ? size : avatarSizes[size] || avatarSizes.small; + const height = width; + const titleSize = width / 2; + const iconSize = width / 2; + const PlaceholderContent = + (renderPlaceholderContent && + renderNode(undefined, renderPlaceholderContent)) || + (title && ( + - - {children} - - ); - }, - { Accessory: Accessory } -); + {title} + + )) || + (icon && ( + + )); + // @ts-ignore + const hidePlaceholder = !(source && source.uri); + // Merge image container style + const imageContainerStyle = StyleSheet.flatten([ + styles.overlayContainer, + rounded && { borderRadius: width / 2, overflow: 'hidden' }, + overlayContainerStyle, + imageProps && imageProps.containerStyle, + ]); + if (imageProps && imageProps.containerStyle) { + delete imageProps.containerStyle; + } + return ( + + + {children} + + ); +}; const styles = StyleSheet.create({ container: { @@ -176,6 +173,7 @@ const styles = StyleSheet.create({ const Avatar = React.memo(AvatarComponent, isEqual); export { Avatar }; -const ThemedAvatar = withTheme(Avatar, 'Avatar'); - +const ThemedAvatar = Object.assign(withTheme(Avatar, 'Avatar'), { + Accessory: Accessory, +}); export default ThemedAvatar; diff --git a/src/avatar/__tests__/__snapshots__/Avatar.js.snap b/src/avatar/__tests__/__snapshots__/Avatar.js.snap index 3576f9dcf2..b21d0e12b4 100644 --- a/src/avatar/__tests__/__snapshots__/Avatar.js.snap +++ b/src/avatar/__tests__/__snapshots__/Avatar.js.snap @@ -10,7 +10,7 @@ exports[`Avatar Component Placeholders renders using icon prop 1`] = ` } } > - - - - - - - - - - - - - - { - Divider: typeof CardDivider; - Image: typeof CardImage; - Title: typeof CardTitle; - FeaturedTitle: typeof CardFeaturedTitle; - FeaturedSubtitle: typeof CardFeaturedSubtitle; -} +interface Card extends React.FunctionComponent {} -const Card: Card = Object.assign( - (props) => { - const { - children, - containerStyle, - wrapperStyle, - theme, - ...attributes - } = props; +const Card: Card = (props) => { + const { + children, + containerStyle, + wrapperStyle, + theme, + ...attributes + } = props; - return ( + return ( + - - {children} - + {children} - ); - }, - { - Divider: CardDivider, - Image: CardImage, - Title: CardTitle, - FeaturedTitle: CardFeaturedTitle, - FeaturedSubtitle: CardFeaturedSubtitle, - } -); + + ); +}; const styles = StyleSheet.create({ wrapper: { @@ -86,5 +71,11 @@ const styles = StyleSheet.create({ export { Card }; -const ThemedCard = withTheme(Card, 'Card'); +const ThemedCard = Object.assign(withTheme(Card, 'Card'), { + Divider: CardDivider, + Image: CardImage, + Title: CardTitle, + FeaturedTitle: CardFeaturedTitle, + FeaturedSubtitle: CardFeaturedSubtitle, +}); export default ThemedCard; diff --git a/src/card/CardFeaturedSubtitle.tsx b/src/card/CardFeaturedSubtitle.tsx index b465b5ee71..23cd8f7323 100644 --- a/src/card/CardFeaturedSubtitle.tsx +++ b/src/card/CardFeaturedSubtitle.tsx @@ -11,6 +11,7 @@ const CardFeaturedSubtitle: React.FunctionComponent = ({ }) => { return ( = ({ }) => { return ( = ({ return ( = (props) => { title && ( { } const WrappedComponent = withTheme(Component); const wrapper = create(); - expect(Object.keys(wrapper.root.children[0].children[0].props)).toContain( - 'theme' - ); + expect(Object.keys(wrapper.root.children[0].props)).toContain('theme'); }); it('passes statics on to wrapped component', () => { @@ -47,8 +45,6 @@ describe('withTheme', () => { } const WrappedComponent = withTheme(Component); const wrapper = create(); - expect(typeof wrapper.root.children[0].children[0].instance.hello).toBe( - 'function' - ); + expect(typeof wrapper.root.children[0].instance.hello).toBe('function'); }); }); diff --git a/src/config/withTheme.tsx b/src/config/withTheme.tsx index 441ff258c8..592830b4cb 100644 --- a/src/config/withTheme.tsx +++ b/src/config/withTheme.tsx @@ -1,21 +1,17 @@ import React from 'react'; import deepmerge from 'deepmerge'; import hoistNonReactStatics from 'hoist-non-react-statics'; -import { ThemeConsumer } from './ThemeProvider'; +import { ThemeConsumer, ThemeProps } from './ThemeProvider'; import DefaultTheme from './theme'; const isClassComponent = (Component: any) => Boolean(Component.prototype && Component.prototype.isReactComponent); -export interface ThemedComponent extends React.FunctionComponent { +export interface ThemedComponent { displayName: string; } -function ThemedComponent( - WrappedComponent, - themeKey, - displayName -): ThemedComponent { +const ThemedComponent = (WrappedComponent, themeKey, displayName) => { return Object.assign( (props) => { // @ts-ignore @@ -54,9 +50,12 @@ function ThemedComponent( }, { displayName: displayName } ); -} +}; -const withTheme = (WrappedComponent: any, themeKey: string) => { +function withTheme

( + WrappedComponent: React.ComponentType

>, + themeKey: string +): React.FunctionComponent>> { const name = themeKey ? `Themed.${themeKey}` : `Themed.${ @@ -65,14 +64,9 @@ const withTheme = (WrappedComponent: any, themeKey: string) => { const Component = ThemedComponent(WrappedComponent, themeKey, name); if (isClassComponent(WrappedComponent)) { - const ClassComponent = Object.setPrototypeOf(Component, WrappedComponent); - const forwardRef = (props, ref) => ( - - ); - - return hoistNonReactStatics(React.forwardRef(forwardRef), WrappedComponent); + return hoistNonReactStatics(Component, WrappedComponent); } - return hoistNonReactStatics(Component, WrappedComponent); -}; + return Component; +} export default withTheme; diff --git a/src/header/Header.tsx b/src/header/Header.tsx index c11b167b39..29c87b10d8 100644 --- a/src/header/Header.tsx +++ b/src/header/Header.tsx @@ -211,4 +211,5 @@ const styles = StyleSheet.create({ }); export { Header }; +//@ts-ignore export default withTheme(Header, 'Header'); diff --git a/src/image/Image.tsx b/src/image/Image.tsx index 7b3f49dbb8..2f73a64290 100644 --- a/src/image/Image.tsx +++ b/src/image/Image.tsx @@ -143,4 +143,5 @@ const styles = StyleSheet.create({ }); export { Image }; +//@ts-ignore export default withTheme(Image, 'Image'); diff --git a/src/input/Input.tsx b/src/input/Input.tsx index e13c23b55d..f8fdebd1ec 100644 --- a/src/input/Input.tsx +++ b/src/input/Input.tsx @@ -231,4 +231,5 @@ const styles = StyleSheet.create({ }); export { Input }; +//@ts-ignore export default withTheme(Input, 'Input'); diff --git a/src/list/ListItem.tsx b/src/list/ListItem.tsx index be5ad5886f..481149393d 100644 --- a/src/list/ListItem.tsx +++ b/src/list/ListItem.tsx @@ -43,78 +43,67 @@ interface ListItem extends React.FunctionComponent { ButtonGroup: typeof ListItemButtonGroup; } -const ListItem: ListItem = Object.assign( - (props) => { - const { - containerStyle, - onPress, - onLongPress, - Component = onPress || onLongPress ? TouchableHighlight : View, - disabled, - disabledStyle, - bottomDivider, - topDivider, - pad = 16, - linearGradientProps, - ViewComponent = View, - theme, - children, - ...attributes - } = props; +const ListItem: ListItem = Object.assign((props) => { + const { + containerStyle, + onPress, + onLongPress, + Component = onPress || onLongPress ? TouchableHighlight : View, + disabled, + disabledStyle, + bottomDivider, + topDivider, + pad = 16, + linearGradientProps, + ViewComponent = View, + theme, + children, + ...attributes + } = props; - if (props.linearGradientProps && !props.ViewComponent) { - console.error( - "You need to pass a ViewComponent to use linearGradientProps !\nExample: ViewComponent={require('react-native-linear-gradient')}" - ); - } - - return ( - - - {children} - - + if (props.linearGradientProps && !props.ViewComponent) { + console.error( + "You need to pass a ViewComponent to use linearGradientProps !\nExample: ViewComponent={require('react-native-linear-gradient')}" ); - }, - { - Chevron: ListItemChevron, - Content: ListItemContent, - Input: ListItemInput, - Title: ListItemTitle, - Subtitle: ListItemSubtitle, - CheckBox: ListItemCheckBox, - ButtonGroup: ListItemButtonGroup, } -); + + return ( + + + {children} + + + ); +}); type PadViewProps = { Component: React.ComponentClass; @@ -155,5 +144,13 @@ class PadView extends React.Component { export { ListItem }; -const ThemedListItem = withTheme(ListItem, 'ListItem'); +const ThemedListItem = Object.assign(withTheme(ListItem, 'ListItem'), { + Chevron: ListItemChevron, + Content: ListItemContent, + Input: ListItemInput, + Title: ListItemTitle, + Subtitle: ListItemSubtitle, + CheckBox: ListItemCheckBox, + ButtonGroup: ListItemButtonGroup, +}); export default ThemedListItem; diff --git a/src/list/__tests__/__snapshots__/ListItem.js.snap b/src/list/__tests__/__snapshots__/ListItem.js.snap index d1addc0850..54c27b0639 100644 --- a/src/list/__tests__/__snapshots__/ListItem.js.snap +++ b/src/list/__tests__/__snapshots__/ListItem.js.snap @@ -15,7 +15,7 @@ exports[`ListItem component should render with avatar 1`] = ` } } > - @@ -37,7 +37,7 @@ exports[`ListItem component should render with input 1`] = ` } } > - @@ -84,8 +84,8 @@ exports[`ListItem component should render with title and subtitle 1`] = ` } } > - - + title test - - + title test - - - + + - title - - + + `; diff --git a/src/pricing/PricingCard.tsx b/src/pricing/PricingCard.tsx index dbb573c54e..6513fe1396 100644 --- a/src/pricing/PricingCard.tsx +++ b/src/pricing/PricingCard.tsx @@ -102,6 +102,7 @@ const PricingCard: React.FunctionComponent = (props) => { {info.map((item) => ( - diff --git a/src/searchbar/SearchBar-android.tsx b/src/searchbar/SearchBar-android.tsx index f0de81d49f..09de8ead7e 100644 --- a/src/searchbar/SearchBar-android.tsx +++ b/src/searchbar/SearchBar-android.tsx @@ -144,6 +144,7 @@ class SearchBar extends Component { onFocus={this.onFocus} onBlur={this.onBlur} onChangeText={this.onChangeText} + //@ts-ignore ref={(input: TextInput) => { this.input = input; }} diff --git a/src/searchbar/SearchBar-default.tsx b/src/searchbar/SearchBar-default.tsx index 97877d597a..3be02ddbc8 100644 --- a/src/searchbar/SearchBar-default.tsx +++ b/src/searchbar/SearchBar-default.tsx @@ -130,6 +130,7 @@ class SearchBar extends React.Component { onFocus={this.onFocus} onBlur={this.onBlur} onChangeText={this.onChangeText} + //@ts-ignore ref={(input: TextInput) => { this.input = input; }} diff --git a/src/searchbar/SearchBar-ios.tsx b/src/searchbar/SearchBar-ios.tsx index 859666e9c1..56a35c390c 100644 --- a/src/searchbar/SearchBar-ios.tsx +++ b/src/searchbar/SearchBar-ios.tsx @@ -179,6 +179,7 @@ class SearchBar extends Component { onFocus={this.onFocus} onBlur={this.onBlur} onChangeText={this.onChangeText} + //@ts-ignore ref={(input: TextInput) => { this.input = input; }} diff --git a/src/searchbar/SearchBar.tsx b/src/searchbar/SearchBar.tsx index 3e98b6f444..0d49399344 100644 --- a/src/searchbar/SearchBar.tsx +++ b/src/searchbar/SearchBar.tsx @@ -81,4 +81,5 @@ class SearchBar extends React.Component { } export { SearchBar }; +//@ts-ignore export default withTheme(SearchBar, 'SearchBar'); diff --git a/src/searchbar/__tests__/__snapshots__/SearchBar-android.js.snap b/src/searchbar/__tests__/__snapshots__/SearchBar-android.js.snap index d1d1077600..bb52aed324 100644 --- a/src/searchbar/__tests__/__snapshots__/SearchBar-android.js.snap +++ b/src/searchbar/__tests__/__snapshots__/SearchBar-android.js.snap @@ -10,7 +10,7 @@ exports[`Android SearchBar component Props cancel button Disabled cancelButtonPr } } > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - = (props) => { > = (props) => { /> {button && title && ( = (props) => { iconContainerStyle && iconContainerStyle, ])} > - {icon && } + {icon && ( + //@ts-ignore + + )} = (props) => { imageProps, ImageComponent, }; + //@ts-ignore return ; } @@ -102,6 +103,7 @@ const Tile: React.FunctionComponent = (props) => { containerStyle && containerStyle, ])} > + {/*@ts-ignore*/} = (props) => { iconContainerStyle && iconContainerStyle, ])} > - {icon && } + {icon && ( + //@ts-ignore + + )} diff --git a/src/tile/__tests__/__snapshots__/Tile.js.snap b/src/tile/__tests__/__snapshots__/Tile.js.snap index 781f53513d..4b52e608e6 100644 --- a/src/tile/__tests__/__snapshots__/Tile.js.snap +++ b/src/tile/__tests__/__snapshots__/Tile.js.snap @@ -9,7 +9,7 @@ exports[`Tile component should apply custom image props 1`] = ` } } > - - + - - + - - + - - + { } export { Tooltip }; +//@ts-ignore export default withTheme(Tooltip, 'Tooltip'); diff --git a/tsconfig.json b/tsconfig.json index 40f8eab584..c84151eec5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { /* Basic Options */ - "target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ - "module": "ES6", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "target": "ES6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "module": "ESnext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": ["ES2017"], /* Specify library files to be included in the compilation. */ "allowJs": true, /* Allow javascript files to be compiled. */ "jsx": "react-native", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ diff --git a/yarn.lock b/yarn.lock index 26da8184df..39646b66e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7807,6 +7807,11 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +utility-types@^3.10.0: + version "3.10.0" + resolved "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" + integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"