forked from grafana/grafana
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathThemeContext.tsx
132 lines (110 loc) · 4.21 KB
/
ThemeContext.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import hoistNonReactStatics from 'hoist-non-react-statics';
import React, { useContext } from 'react';
import { createTheme, GrafanaTheme, GrafanaTheme2 } from '@grafana/data';
import { Themeable, Themeable2 } from '../types/theme';
import { stylesFactory } from './stylesFactory';
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
type Subtract<T, K> = Omit<T, keyof K>;
/**
* Mock used in tests
*/
let ThemeContextMock: React.Context<GrafanaTheme2> | null = null;
// Used by useStyles()
export const memoizedStyleCreators = new WeakMap();
// Use Grafana Dark theme by default
/** @public */
export const ThemeContext = React.createContext(createTheme());
ThemeContext.displayName = 'ThemeContext';
/** @deprecated use withTheme2 */
/** @public */
export const withTheme = <P extends Themeable, S extends {} = {}>(Component: React.ComponentType<P>) => {
const WithTheme: React.FunctionComponent<Subtract<P, Themeable>> = (props) => {
/**
* If theme context is mocked, let's use it instead of the original context
* This is used in tests when mocking theme using mockThemeContext function defined below
*/
const ContextComponent = ThemeContextMock || ThemeContext;
return (
// @ts-ignore
<ContextComponent.Consumer>{(theme) => <Component {...props} theme={theme.v1} />}</ContextComponent.Consumer>
);
};
WithTheme.displayName = `WithTheme(${Component.displayName})`;
// @ts-ignore
hoistNonReactStatics(WithTheme, Component);
type Hoisted = typeof WithTheme & S;
return WithTheme as Hoisted;
};
/** @alpha */
export const withTheme2 = <P extends Themeable2, S extends {} = {}>(Component: React.ComponentType<P>) => {
const WithTheme: React.FunctionComponent<Subtract<P, Themeable2>> = (props) => {
/**
* If theme context is mocked, let's use it instead of the original context
* This is used in tests when mocking theme using mockThemeContext function defined below
*/
const ContextComponent = ThemeContextMock || ThemeContext;
return (
// @ts-ignore
<ContextComponent.Consumer>{(theme) => <Component {...props} theme={theme} />}</ContextComponent.Consumer>
);
};
WithTheme.displayName = `WithTheme(${Component.displayName})`;
// @ts-ignore
hoistNonReactStatics(WithTheme, Component);
type Hoisted = typeof WithTheme & S;
return WithTheme as Hoisted;
};
/** @deprecated use useTheme2 */
/** @public */
export function useTheme(): GrafanaTheme {
return useContext(ThemeContextMock || ThemeContext).v1;
}
/** @public */
export function useTheme2(): GrafanaTheme2 {
return useContext(ThemeContextMock || ThemeContext);
}
/**
* Hook for using memoized styles with access to the theme.
*
* NOTE: For memoization to work, you need to ensure that the function
* you pass in doesn't change, or only if it needs to. (i.e. declare
* your style creator outside of a function component or use `useCallback()`.)
* */
/** @deprecated use useStyles2 */
/** @public */
export function useStyles<T>(getStyles: (theme: GrafanaTheme) => T) {
const theme = useTheme();
let memoizedStyleCreator = memoizedStyleCreators.get(getStyles) as typeof getStyles;
if (!memoizedStyleCreator) {
memoizedStyleCreator = stylesFactory(getStyles);
memoizedStyleCreators.set(getStyles, memoizedStyleCreator);
}
return memoizedStyleCreator(theme);
}
/**
* Hook for using memoized styles with access to the theme.
*
* NOTE: For memoization to work, you need to ensure that the function
* you pass in doesn't change, or only if it needs to. (i.e. declare
* your style creator outside of a function component or use `useCallback()`.)
* */
/** @public */
export function useStyles2<T>(getStyles: (theme: GrafanaTheme2) => T) {
const theme = useTheme2();
let memoizedStyleCreator = memoizedStyleCreators.get(getStyles) as typeof getStyles;
if (!memoizedStyleCreator) {
memoizedStyleCreator = stylesFactory(getStyles);
memoizedStyleCreators.set(getStyles, memoizedStyleCreator);
}
return memoizedStyleCreator(theme);
}
/**
* Enables theme context mocking
*/
/** @public */
export const mockThemeContext = (theme: Partial<GrafanaTheme2>) => {
ThemeContextMock = React.createContext(theme as GrafanaTheme2);
return () => {
ThemeContextMock = null;
};
};