Skip to content

Commit dbe3068

Browse files
satya164souhe
authored andcommitted
BREAKING: automatically determine partial theme type (#32)
1 parent b284db6 commit dbe3068

File tree

5 files changed

+38
-48
lines changed

5 files changed

+38
-48
lines changed

src/createThemeProvider.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,16 @@ import * as React from 'react';
44

55
import type { Context } from 'create-react-context';
66

7-
type ThemeProviderProps<T> = {
8-
children?: any,
9-
theme: T,
10-
};
11-
12-
export type ThemeProviderType<T> = React.ComponentType<ThemeProviderProps<T>>;
7+
export type ThemeProviderType<T> = React.ComponentType<{
8+
children: React.Node,
9+
theme?: T,
10+
}>;
1311

1412
function createThemeProvider<T>(
1513
defaultTheme: T,
1614
ThemeContext: Context<T>
1715
): ThemeProviderType<T> {
18-
return class ThemeProvider extends React.PureComponent<
19-
ThemeProviderProps<T>
20-
> {
16+
return class ThemeProvider extends React.Component<*> {
2117
static defaultProps = {
2218
theme: defaultTheme,
2319
};

src/createTheming.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ import createWithTheme from './createWithTheme';
66
import type { WithThemeType } from './createWithTheme';
77
import type { ThemeProviderType } from './createThemeProvider';
88

9-
export type ThemingType<T, S> = {
9+
export type ThemingType<T> = {
1010
ThemeProvider: ThemeProviderType<T>,
11-
withTheme: WithThemeType<T, S>,
11+
withTheme: WithThemeType<T>,
1212
};
1313

14-
export default function createTheming<T, S>(
14+
export default function createTheming<T: Object>(
1515
defaultTheme: T
16-
): ThemingType<T, S> {
16+
): ThemingType<T> {
1717
const ThemeContext: Context<T> = createReactContext(defaultTheme);
1818

1919
const ThemeProvider: ThemeProviderType<T> = createThemeProvider(
2020
defaultTheme,
2121
ThemeContext
2222
);
23-
const withTheme: WithThemeType<T, S> = createWithTheme(
23+
const withTheme: WithThemeType<T> = createWithTheme(
2424
ThemeProvider,
2525
ThemeContext
2626
);

src/createWithTheme.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,21 @@ import { copyRefs } from './utils';
1010

1111
import type { ThemeProviderType } from './createThemeProvider';
1212

13+
type $DeepShape<O: Object> = $Shape<
14+
$ObjMap<O, (<V: Object>(V) => $DeepShape<V>) & (<V>(V) => V)>
15+
>;
16+
1317
const isClassComponent = (Component: any) =>
1418
Boolean(Component.prototype && Component.prototype.isReactComponent);
1519

16-
export type WithThemeType<T, S> = <P, C: React.ComponentType<P>>(
20+
export type WithThemeType<T> = <P, C: React.ComponentType<P>>(
1721
Comp: C
1822
) => C &
1923
React.ComponentType<
20-
$Diff<React.ElementConfig<C>, { theme: T }> & { theme?: S }
24+
$Diff<React.ElementConfig<C>, { theme: T }> & { theme?: $DeepShape<T> }
2125
>;
2226

23-
const createWithTheme = <T, S>(
27+
const createWithTheme = <T: Object, S: $DeepShape<T>>(
2428
ThemeProvider: ThemeProviderType<T>,
2529
ThemeContext: Context<T>
2630
) =>
@@ -47,7 +51,6 @@ const createWithTheme = <T, S>(
4751
_root: any;
4852

4953
render() {
50-
const { forwardedRef, ...rest } = this.props;
5154
return (
5255
<ThemeContext.Consumer>
5356
{theme => {
@@ -59,15 +62,15 @@ const createWithTheme = <T, S>(
5962
// It's needed to support use cases which need access to the underlying node
6063
element = (
6164
<Comp
62-
{...rest}
65+
{...this.props}
6366
ref={c => {
6467
this._root = c;
6568
}}
6669
theme={merged}
6770
/>
6871
);
6972
} else {
70-
element = <Comp {...rest} theme={merged} />;
73+
element = <Comp {...this.props} theme={merged} />;
7174
}
7275

7376
if (merged !== this.props.theme) {
@@ -82,24 +85,23 @@ const createWithTheme = <T, S>(
8285
}
8386
}
8487

85-
let ComponentWithMethods = ThemedComponent;
8688
if (isClassComponent(Comp)) {
8789
// getWrappedInstance is exposed by some HOCs like react-redux's connect
8890
// Use it to get the ref to the underlying element
8991
// Also expose it to access the underlying element after wrapping
9092
// $FlowFixMe
91-
ComponentWithMethods.prototype.getWrappedInstance = function getWrappedInstance() {
92-
return this._root.getWrappedInstance
93+
ThemedComponent.prototype.getWrappedInstance = function getWrappedInstance() {
94+
return this._root && this._root.getWrappedInstance
9395
? this._root.getWrappedInstance()
9496
: this._root;
9597
};
9698

97-
ComponentWithMethods = copyRefs(ComponentWithMethods, Comp);
99+
ThemedComponent = copyRefs(ThemedComponent, Comp);
98100
}
99101

100-
hoistNonReactStatics(ComponentWithMethods, Comp);
102+
hoistNonReactStatics(ThemedComponent, Comp);
101103

102-
return (ComponentWithMethods: any);
104+
return (ThemedComponent: any);
103105
};
104106

105107
export default createWithTheme;

typings/__tests__/index.test.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
import * as React from "react";
22
import { createTheming } from "../..";
33

4-
export type Theme = {
4+
type Theme = {
55
primaryColor: string;
66
accentColor: string;
77
backgroundColor: string;
88
textColor: string;
99
secondaryColor: string;
1010
};
1111

12-
export type PartialTheme = {
13-
primaryColor?: string;
14-
};
15-
16-
export const themes: { [key: string]: Theme } = {
12+
const themes: { [key: string]: Theme } = {
1713
default: {
1814
primaryColor: "#FFA72A",
1915
accentColor: "#458622",
@@ -30,7 +26,7 @@ export const themes: { [key: string]: Theme } = {
3026
}
3127
};
3228

33-
const { ThemeProvider, withTheme } = createTheming<Theme, PartialTheme>(
29+
const { ThemeProvider, withTheme } = createTheming<Theme>(
3430
themes.default
3531
);
3632

typings/index.d.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
// Type definitions for @callstack/react-theme-provider 1.0.2
22
// TypeScript version 3.0.3
33

4-
import * as React from "react";
4+
import * as React from 'react';
55

6-
type Without<T, K> = Pick<T, Exclude<keyof T, K>>;
6+
type $Without<T, K> = Pick<T, Exclude<keyof T, K>>;
7+
type $DeepPartial<T> = { [P in keyof T]?: $DeepPartial<T[P]> };
78

8-
export type ThemeProviderType<Theme> = React.ComponentType<{ theme: Theme }>;
9-
10-
export type ThemingType<Theme, PartialTheme> = {
11-
ThemeProvider: ThemeProviderType<Theme>;
9+
export type ThemingType<Theme> = {
10+
ThemeProvider: React.ComponentType<{ theme?: Theme }>;
1211
withTheme: <Props extends { theme: Theme }>(
1312
Comp: React.ComponentType<Props>
14-
) => React.ComponentType<Without<Props, "theme"> & { theme?: PartialTheme }>;
13+
) => React.ComponentType<
14+
$Without<Props, 'theme'> & { theme?: $DeepPartial<Theme> }
15+
>;
1516
};
1617

1718
// Library exports
18-
export const ThemeProvider: ThemeProviderType<{}>;
19-
20-
export const withTheme: <Props extends { theme: {} }>(
21-
Comp: React.ComponentType<Props>
22-
) => React.ComponentType<Without<Props, "theme">>;
19+
export const ThemeProvider: ThemingType<object>['ThemeProvider'];
20+
export const withTheme: ThemingType<object>['withTheme'];
2321

24-
export const createTheming: <Theme, PartialTheme>(
25-
defaultTheme: Theme
26-
) => ThemingType<Theme, PartialTheme>;
22+
export const createTheming: <Theme>(defaultTheme: Theme) => ThemingType<Theme>;

0 commit comments

Comments
 (0)