@wise/components-theming 0.1.2-next-b9f447bdaa.2571 → 0.1.2-next-9ff5b52e35.2579
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.d.ts +5 -4
- package/dist/cjs/index.js +52 -30
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.d.ts +5 -4
- package/dist/es/index.js +54 -32
- package/dist/es/index.js.map +1 -1
- package/package.json +2 -2
- package/src/ThemeProvider.spec.tsx +21 -50
- package/src/ThemeProvider.tsx +20 -26
- package/src/ThemeScript.tsx +18 -0
- package/src/ThemedChildren.tsx +11 -0
- package/src/__snapshots__/ThemeProvider.spec.tsx.snap +26 -0
- package/src/const.ts +4 -0
- package/src/index.ts +1 -1
package/dist/cjs/index.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ declare const componentThemes: readonly [
|
|
|
5
5
|
"personal"
|
|
6
6
|
];
|
|
7
7
|
type ComponentTheme = (typeof componentThemes)[number];
|
|
8
|
-
type
|
|
9
|
-
theme
|
|
10
|
-
}
|
|
8
|
+
type Theming = {
|
|
9
|
+
theme?: ComponentTheme;
|
|
10
|
+
};
|
|
11
|
+
type ThemeProviderProps = PropsWithChildren<Theming>;
|
|
11
12
|
declare const ThemeProvider: ({ theme, children }: ThemeProviderProps) => JSX.Element;
|
|
12
13
|
interface ThemeHookValue {
|
|
13
14
|
theme: ComponentTheme;
|
|
@@ -16,5 +17,5 @@ interface ThemeHookValue {
|
|
|
16
17
|
className: string;
|
|
17
18
|
}
|
|
18
19
|
declare const useTheme: () => ThemeHookValue;
|
|
19
|
-
export type { ComponentTheme };
|
|
20
|
+
export type { ComponentTheme, Theming };
|
|
20
21
|
export { ThemeProvider, useTheme };
|
package/dist/cjs/index.js
CHANGED
|
@@ -7,36 +7,6 @@ var modernThemes = ['personal'];
|
|
|
7
7
|
var darkThemes = [];
|
|
8
8
|
var DEFAULT_COMPONENT_THEME = 'light';
|
|
9
9
|
|
|
10
|
-
var ThemeContext = /*#__PURE__*/react.createContext(DEFAULT_COMPONENT_THEME);
|
|
11
|
-
var ThemeProvider = function ThemeProvider(_ref) {
|
|
12
|
-
var _ref$theme = _ref.theme,
|
|
13
|
-
theme = _ref$theme === void 0 ? DEFAULT_COMPONENT_THEME : _ref$theme,
|
|
14
|
-
children = _ref.children;
|
|
15
|
-
var themedChildren = react.Children.map(children, function (child) {
|
|
16
|
-
var _child$props;
|
|
17
|
-
|
|
18
|
-
if (! /*#__PURE__*/react.isValidElement(child)) {
|
|
19
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
20
|
-
// eslint-disable-next-line no-console
|
|
21
|
-
console.warn('[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return child;
|
|
25
|
-
} // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
var classNames = ["np-theme-".concat(theme), child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.className].filter(Boolean).join(' ');
|
|
29
|
-
return /*#__PURE__*/react.cloneElement(child, {
|
|
30
|
-
className: classNames // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
-
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
return jsxRuntime.jsx(ThemeContext.Provider, {
|
|
35
|
-
value: theme,
|
|
36
|
-
children: themedChildren
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
|
-
|
|
40
10
|
var isThemeModern = function isThemeModern(theme) {
|
|
41
11
|
return modernThemes.includes(theme);
|
|
42
12
|
};
|
|
@@ -61,6 +31,58 @@ var useTheme = function useTheme() {
|
|
|
61
31
|
}, [theme]);
|
|
62
32
|
};
|
|
63
33
|
|
|
34
|
+
var ThemeScript = /*#__PURE__*/react.memo(function () {
|
|
35
|
+
var _useTheme = useTheme(),
|
|
36
|
+
className = _useTheme.className;
|
|
37
|
+
|
|
38
|
+
var scriptSource = react.useMemo(function () {
|
|
39
|
+
return "!(function () {try {document.documentElement.classList.add('".concat(className, "');} catch (e) {}})();");
|
|
40
|
+
}, [className]); // eslint-disable-next-line react/no-danger
|
|
41
|
+
|
|
42
|
+
return jsxRuntime.jsx("script", {
|
|
43
|
+
dangerouslySetInnerHTML: {
|
|
44
|
+
__html: scriptSource
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}, // Never re-render this component
|
|
48
|
+
function () {
|
|
49
|
+
return true;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
var ThemedChildren = function ThemedChildren(_ref) {
|
|
53
|
+
var children = _ref.children;
|
|
54
|
+
|
|
55
|
+
var _useTheme = useTheme(),
|
|
56
|
+
className = _useTheme.className;
|
|
57
|
+
|
|
58
|
+
return jsxRuntime.jsx("div", {
|
|
59
|
+
className: className,
|
|
60
|
+
children: children
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
var ThemeContext = /*#__PURE__*/react.createContext(undefined);
|
|
65
|
+
var ThemeProvider = function ThemeProvider(_ref) {
|
|
66
|
+
var _ref$theme = _ref.theme,
|
|
67
|
+
theme = _ref$theme === void 0 ? DEFAULT_COMPONENT_THEME : _ref$theme,
|
|
68
|
+
children = _ref.children;
|
|
69
|
+
var isRootProvider = react.useContext(ThemeContext) === undefined;
|
|
70
|
+
|
|
71
|
+
if (isRootProvider) {
|
|
72
|
+
return jsxRuntime.jsxs(ThemeContext.Provider, {
|
|
73
|
+
value: theme,
|
|
74
|
+
children: [jsxRuntime.jsx(ThemeScript, {}), children]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return jsxRuntime.jsx(ThemeContext.Provider, {
|
|
79
|
+
value: theme,
|
|
80
|
+
children: jsxRuntime.jsx(ThemedChildren, {
|
|
81
|
+
children: children
|
|
82
|
+
})
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
64
86
|
exports.ThemeProvider = ThemeProvider;
|
|
65
87
|
exports.useTheme = useTheme;
|
|
66
88
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/const.ts","../../src/
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/const.ts","../../src/helpers.ts","../../src/useTheme.ts","../../src/ThemeScript.tsx","../../src/ThemedChildren.tsx","../../src/ThemeProvider.tsx"],"sourcesContent":["export const componentThemes = ['light', 'personal'] as const;\nexport const modernThemes = ['personal'] as const;\nexport const darkThemes = [] as const;\n\nexport type ComponentTheme = typeof componentThemes[number];\nexport type ModernTheme = typeof modernThemes[number];\nexport type DarkTheme = typeof darkThemes[number];\n\nexport const DEFAULT_COMPONENT_THEME = 'light' as const;\n\nexport type Theming = {\n theme?: ComponentTheme;\n};\n","import { ComponentTheme, DarkTheme, ModernTheme, modernThemes, darkThemes } from './const';\n\nexport const isThemeModern = (theme: ComponentTheme): theme is ModernTheme =>\n modernThemes.includes(theme as ModernTheme);\n\nexport const isThemeDark = (theme: ComponentTheme): theme is DarkTheme =>\n darkThemes.includes(theme as DarkTheme);\n\nexport const getThemeClassName = (theme: ComponentTheme) => `np-theme-${theme}`;\n","import { useContext, useMemo } from 'react';\n\nimport { ThemeContext } from './ThemeProvider';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME } from './const';\nimport { getThemeClassName, isThemeDark, isThemeModern } from './helpers';\n\ninterface ThemeHookValue {\n theme: ComponentTheme;\n isModern: boolean;\n isDark: boolean;\n className: string;\n}\n\nexport const useTheme = (): ThemeHookValue => {\n const theme = useContext(ThemeContext) ?? DEFAULT_COMPONENT_THEME;\n\n return useMemo(\n () => ({\n theme,\n isModern: isThemeModern(theme),\n isDark: isThemeDark(theme),\n className: getThemeClassName(theme),\n }),\n [theme],\n );\n};\n","import { memo, useMemo } from 'react';\n\nimport { useTheme } from './useTheme';\n\nexport const ThemeScript = memo(\n () => {\n const { className } = useTheme();\n\n const scriptSource = useMemo(() => {\n return `!(function () {try {document.documentElement.classList.add('${className}');} catch (e) {}})();`;\n }, [className]);\n\n // eslint-disable-next-line react/no-danger\n return <script dangerouslySetInnerHTML={{ __html: scriptSource }} />;\n },\n // Never re-render this component\n () => true,\n);\n","import { ReactNode } from 'react';\n\nimport { useTheme } from './useTheme';\n\ntype ThemedChildrenProps = { children: ReactNode };\n\nexport const ThemedChildren = ({ children }: ThemedChildrenProps) => {\n const { className } = useTheme();\n\n return <div className={className}>{children}</div>;\n};\n","import { createContext, PropsWithChildren, useContext } from 'react';\n\nimport { ThemeScript } from './ThemeScript';\nimport { ThemedChildren } from './ThemedChildren';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME, Theming } from './const';\n\nexport const ThemeContext = createContext<ComponentTheme | undefined>(undefined);\n\ntype ThemeProviderProps = PropsWithChildren<Theming>;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_COMPONENT_THEME,\n children,\n}: ThemeProviderProps) => {\n const isRootProvider = useContext(ThemeContext) === undefined;\n\n if (isRootProvider) {\n return (\n <ThemeContext.Provider value={theme}>\n <ThemeScript />\n {children}\n </ThemeContext.Provider>\n );\n }\n\n return (\n <ThemeContext.Provider value={theme}>\n <ThemedChildren>{children}</ThemedChildren>\n </ThemeContext.Provider>\n );\n};\n"],"names":["modernThemes","darkThemes","DEFAULT_COMPONENT_THEME","isThemeModern","theme","includes","isThemeDark","getThemeClassName","useTheme","useContext","ThemeContext","useMemo","isModern","isDark","className","ThemeScript","memo","scriptSource","_jsx","dangerouslySetInnerHTML","__html","ThemedChildren","children","createContext","undefined","ThemeProvider","isRootProvider","_jsxs","Provider","value"],"mappings":";;;;;AACO,IAAMA,YAAY,GAAG,CAAC,UAAD,CAArB,CAAA;AACA,IAAMC,UAAU,GAAG,EAAnB,CAAA;AAMA,IAAMC,uBAAuB,GAAG,OAAhC;;ACNA,IAAMC,aAAa,GAAG,SAAhBA,aAAgB,CAACC,KAAD,EAAA;AAAA,EAAA,OAC3BJ,YAAY,CAACK,QAAb,CAAsBD,KAAtB,CAD2B,CAAA;AAAA,CAAtB,CAAA;AAGA,IAAME,WAAW,GAAG,SAAdA,WAAc,CAACF,KAAD,EAAA;AAAA,EAAA,OACzBH,UAAU,CAACI,QAAX,CAAoBD,KAApB,CADyB,CAAA;AAAA,CAApB,CAAA;AAGA,IAAMG,iBAAiB,GAAG,SAApBA,iBAAoB,CAACH,KAAD,EAAA;AAAA,EAAA,OAAA,WAAA,CAAA,MAAA,CAAuCA,KAAvC,CAAA,CAAA;AAAA,CAA1B;;ACKMI,IAAAA,QAAQ,GAAG,SAAXA,QAAW,GAAqB;AAAA,EAAA,IAAA,WAAA,CAAA;;AAC3C,EAAA,IAAMJ,KAAK,GAAGK,CAAAA,WAAAA,GAAAA,gBAAU,CAACC,YAAD,CAAb,qDAA+BR,uBAA1C,CAAA;AAEA,EAAA,OAAOS,aAAO,CACZ,YAAA;IAAA,OAAO;AACLP,MAAAA,KAAK,EAALA,KADK;AAELQ,MAAAA,QAAQ,EAAET,aAAa,CAACC,KAAD,CAFlB;AAGLS,MAAAA,MAAM,EAAEP,WAAW,CAACF,KAAD,CAHd;MAILU,SAAS,EAAEP,iBAAiB,CAACH,KAAD,CAAA;KAJ9B,CAAA;AAAA,GADY,EAOZ,CAACA,KAAD,CAPY,CAAd,CAAA;AASD;;ACrBM,IAAMW,WAAW,gBAAGC,UAAI,CAC7B,YAAK;AACH,EAAA,IAAA,SAAA,GAAsBR,QAAQ,EAA9B;MAAQM,SAAR,aAAQA,SAAR,CAAA;;AAEA,EAAA,IAAMG,YAAY,GAAGN,aAAO,CAAC,YAAK;AAChC,IAAA,OAAA,8DAAA,CAAA,MAAA,CAAsEG,SAAtE,EAAA,wBAAA,CAAA,CAAA;AACD,GAF2B,EAEzB,CAACA,SAAD,CAFyB,CAA5B,CAHG;;EAQH,OAAOI,cAAAA,CAAAA,QAAAA,EAAAA;AAAQC,IAAAA,uBAAuB,EAAE;AAAEC,MAAAA,MAAM,EAAEH,YAAAA;AAAV,KAAA;GAAxC,CAAA,CAAA;AACD,CAV4B;AAY7B,YAAA;AAAA,EAAA,OAAM,IAAN,CAAA;AAAA,CAZ6B,CAAxB;;ACEA,IAAMI,cAAc,GAAG,SAAjBA,cAAiB,CAAsC,IAAA,EAAA;EAAA,IAAnCC,QAAmC,QAAnCA,QAAmC,CAAA;;AAClE,EAAA,IAAA,SAAA,GAAsBd,QAAQ,EAA9B;MAAQM,SAAR,aAAQA,SAAR,CAAA;;EAEA,OAAOI,cAAAA,CAAAA,KAAAA,EAAAA;AAAKJ,IAAAA,SAAS,EAAEA,SAAhB;IAAyBQ,QAAGA,EAAAA,QAAAA;GAAnC,CAAA,CAAA;AACD,CAJM;;ACAA,IAAMZ,YAAY,gBAAGa,mBAAa,CAA6BC,SAA7B,CAAlC,CAAA;AAIMC,IAAAA,aAAa,GAAG,SAAhBA,aAAgB,CAGJ,IAAA,EAAA;AAAA,EAAA,IAAA,UAAA,GAAA,IAAA,CAFvBrB,KAEuB;MAFvBA,KAEuB,2BAFfF,uBAEe,GAAA,UAAA;MADvBoB,QACuB,QADvBA,QACuB,CAAA;AACvB,EAAA,IAAMI,cAAc,GAAGjB,gBAAU,CAACC,YAAD,CAAV,KAA6Bc,SAApD,CAAA;;AAEA,EAAA,IAAIE,cAAJ,EAAoB;AAClB,IAAA,OACEC,eAACjB,CAAAA,YAAY,CAACkB,QAAd,EAAsB;AAACC,MAAAA,KAAK,EAAEzB,KAAR;AAAakB,MAAAA,QAAAA,EAAAA,CACjCJ,eAACH,WAAD,EAAY,EAAZ,CADiC,EAEhCO,QAFgC,CAAA;AAAb,KAAtB,CADF,CAAA;AAMD,GAAA;;AAED,EAAA,OACEJ,cAACR,CAAAA,YAAY,CAACkB,QAAd,EAAsB;AAACC,IAAAA,KAAK,EAAEzB,KAAR;IAAakB,QACjCJ,EAAAA,cAAAA,CAACG,cAAD,EAAe;MAAAC,QAAEA,EAAAA,QAAAA;KAAjB,CAAA;AADoB,GAAtB,CADF,CAAA;AAKD;;;;;"}
|
package/dist/es/index.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ declare const componentThemes: readonly [
|
|
|
5
5
|
"personal"
|
|
6
6
|
];
|
|
7
7
|
type ComponentTheme = (typeof componentThemes)[number];
|
|
8
|
-
type
|
|
9
|
-
theme
|
|
10
|
-
}
|
|
8
|
+
type Theming = {
|
|
9
|
+
theme?: ComponentTheme;
|
|
10
|
+
};
|
|
11
|
+
type ThemeProviderProps = PropsWithChildren<Theming>;
|
|
11
12
|
declare const ThemeProvider: ({ theme, children }: ThemeProviderProps) => JSX.Element;
|
|
12
13
|
interface ThemeHookValue {
|
|
13
14
|
theme: ComponentTheme;
|
|
@@ -16,5 +17,5 @@ interface ThemeHookValue {
|
|
|
16
17
|
className: string;
|
|
17
18
|
}
|
|
18
19
|
declare const useTheme: () => ThemeHookValue;
|
|
19
|
-
export type { ComponentTheme };
|
|
20
|
+
export type { ComponentTheme, Theming };
|
|
20
21
|
export { ThemeProvider, useTheme };
|
package/dist/es/index.js
CHANGED
|
@@ -1,40 +1,10 @@
|
|
|
1
|
-
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { useContext, useMemo, memo, createContext } from 'react';
|
|
3
3
|
|
|
4
4
|
var modernThemes = ['personal'];
|
|
5
5
|
var darkThemes = [];
|
|
6
6
|
var DEFAULT_COMPONENT_THEME = 'light';
|
|
7
7
|
|
|
8
|
-
var ThemeContext = /*#__PURE__*/createContext(DEFAULT_COMPONENT_THEME);
|
|
9
|
-
var ThemeProvider = function ThemeProvider(_ref) {
|
|
10
|
-
var _ref$theme = _ref.theme,
|
|
11
|
-
theme = _ref$theme === void 0 ? DEFAULT_COMPONENT_THEME : _ref$theme,
|
|
12
|
-
children = _ref.children;
|
|
13
|
-
var themedChildren = Children.map(children, function (child) {
|
|
14
|
-
var _child$props;
|
|
15
|
-
|
|
16
|
-
if (! /*#__PURE__*/isValidElement(child)) {
|
|
17
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
18
|
-
// eslint-disable-next-line no-console
|
|
19
|
-
console.warn('[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return child;
|
|
23
|
-
} // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
var classNames = ["np-theme-".concat(theme), child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.className].filter(Boolean).join(' ');
|
|
27
|
-
return /*#__PURE__*/cloneElement(child, {
|
|
28
|
-
className: classNames // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
return jsx(ThemeContext.Provider, {
|
|
33
|
-
value: theme,
|
|
34
|
-
children: themedChildren
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
|
|
38
8
|
var isThemeModern = function isThemeModern(theme) {
|
|
39
9
|
return modernThemes.includes(theme);
|
|
40
10
|
};
|
|
@@ -59,5 +29,57 @@ var useTheme = function useTheme() {
|
|
|
59
29
|
}, [theme]);
|
|
60
30
|
};
|
|
61
31
|
|
|
32
|
+
var ThemeScript = /*#__PURE__*/memo(function () {
|
|
33
|
+
var _useTheme = useTheme(),
|
|
34
|
+
className = _useTheme.className;
|
|
35
|
+
|
|
36
|
+
var scriptSource = useMemo(function () {
|
|
37
|
+
return "!(function () {try {document.documentElement.classList.add('".concat(className, "');} catch (e) {}})();");
|
|
38
|
+
}, [className]); // eslint-disable-next-line react/no-danger
|
|
39
|
+
|
|
40
|
+
return jsx("script", {
|
|
41
|
+
dangerouslySetInnerHTML: {
|
|
42
|
+
__html: scriptSource
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}, // Never re-render this component
|
|
46
|
+
function () {
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
var ThemedChildren = function ThemedChildren(_ref) {
|
|
51
|
+
var children = _ref.children;
|
|
52
|
+
|
|
53
|
+
var _useTheme = useTheme(),
|
|
54
|
+
className = _useTheme.className;
|
|
55
|
+
|
|
56
|
+
return jsx("div", {
|
|
57
|
+
className: className,
|
|
58
|
+
children: children
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
var ThemeContext = /*#__PURE__*/createContext(undefined);
|
|
63
|
+
var ThemeProvider = function ThemeProvider(_ref) {
|
|
64
|
+
var _ref$theme = _ref.theme,
|
|
65
|
+
theme = _ref$theme === void 0 ? DEFAULT_COMPONENT_THEME : _ref$theme,
|
|
66
|
+
children = _ref.children;
|
|
67
|
+
var isRootProvider = useContext(ThemeContext) === undefined;
|
|
68
|
+
|
|
69
|
+
if (isRootProvider) {
|
|
70
|
+
return jsxs(ThemeContext.Provider, {
|
|
71
|
+
value: theme,
|
|
72
|
+
children: [jsx(ThemeScript, {}), children]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return jsx(ThemeContext.Provider, {
|
|
77
|
+
value: theme,
|
|
78
|
+
children: jsx(ThemedChildren, {
|
|
79
|
+
children: children
|
|
80
|
+
})
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
62
84
|
export { ThemeProvider, useTheme };
|
|
63
85
|
//# sourceMappingURL=index.js.map
|
package/dist/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/const.ts","../../src/
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/const.ts","../../src/helpers.ts","../../src/useTheme.ts","../../src/ThemeScript.tsx","../../src/ThemedChildren.tsx","../../src/ThemeProvider.tsx"],"sourcesContent":["export const componentThemes = ['light', 'personal'] as const;\nexport const modernThemes = ['personal'] as const;\nexport const darkThemes = [] as const;\n\nexport type ComponentTheme = typeof componentThemes[number];\nexport type ModernTheme = typeof modernThemes[number];\nexport type DarkTheme = typeof darkThemes[number];\n\nexport const DEFAULT_COMPONENT_THEME = 'light' as const;\n\nexport type Theming = {\n theme?: ComponentTheme;\n};\n","import { ComponentTheme, DarkTheme, ModernTheme, modernThemes, darkThemes } from './const';\n\nexport const isThemeModern = (theme: ComponentTheme): theme is ModernTheme =>\n modernThemes.includes(theme as ModernTheme);\n\nexport const isThemeDark = (theme: ComponentTheme): theme is DarkTheme =>\n darkThemes.includes(theme as DarkTheme);\n\nexport const getThemeClassName = (theme: ComponentTheme) => `np-theme-${theme}`;\n","import { useContext, useMemo } from 'react';\n\nimport { ThemeContext } from './ThemeProvider';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME } from './const';\nimport { getThemeClassName, isThemeDark, isThemeModern } from './helpers';\n\ninterface ThemeHookValue {\n theme: ComponentTheme;\n isModern: boolean;\n isDark: boolean;\n className: string;\n}\n\nexport const useTheme = (): ThemeHookValue => {\n const theme = useContext(ThemeContext) ?? DEFAULT_COMPONENT_THEME;\n\n return useMemo(\n () => ({\n theme,\n isModern: isThemeModern(theme),\n isDark: isThemeDark(theme),\n className: getThemeClassName(theme),\n }),\n [theme],\n );\n};\n","import { memo, useMemo } from 'react';\n\nimport { useTheme } from './useTheme';\n\nexport const ThemeScript = memo(\n () => {\n const { className } = useTheme();\n\n const scriptSource = useMemo(() => {\n return `!(function () {try {document.documentElement.classList.add('${className}');} catch (e) {}})();`;\n }, [className]);\n\n // eslint-disable-next-line react/no-danger\n return <script dangerouslySetInnerHTML={{ __html: scriptSource }} />;\n },\n // Never re-render this component\n () => true,\n);\n","import { ReactNode } from 'react';\n\nimport { useTheme } from './useTheme';\n\ntype ThemedChildrenProps = { children: ReactNode };\n\nexport const ThemedChildren = ({ children }: ThemedChildrenProps) => {\n const { className } = useTheme();\n\n return <div className={className}>{children}</div>;\n};\n","import { createContext, PropsWithChildren, useContext } from 'react';\n\nimport { ThemeScript } from './ThemeScript';\nimport { ThemedChildren } from './ThemedChildren';\nimport { ComponentTheme, DEFAULT_COMPONENT_THEME, Theming } from './const';\n\nexport const ThemeContext = createContext<ComponentTheme | undefined>(undefined);\n\ntype ThemeProviderProps = PropsWithChildren<Theming>;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_COMPONENT_THEME,\n children,\n}: ThemeProviderProps) => {\n const isRootProvider = useContext(ThemeContext) === undefined;\n\n if (isRootProvider) {\n return (\n <ThemeContext.Provider value={theme}>\n <ThemeScript />\n {children}\n </ThemeContext.Provider>\n );\n }\n\n return (\n <ThemeContext.Provider value={theme}>\n <ThemedChildren>{children}</ThemedChildren>\n </ThemeContext.Provider>\n );\n};\n"],"names":["modernThemes","darkThemes","DEFAULT_COMPONENT_THEME","isThemeModern","theme","includes","isThemeDark","getThemeClassName","useTheme","useContext","ThemeContext","useMemo","isModern","isDark","className","ThemeScript","memo","scriptSource","_jsx","dangerouslySetInnerHTML","__html","ThemedChildren","children","createContext","undefined","ThemeProvider","isRootProvider","_jsxs","Provider","value"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wise/components-theming",
|
|
3
|
-
"version": "0.1.2-next-
|
|
3
|
+
"version": "0.1.2-next-9ff5b52e35.2579+9ff5b52e35",
|
|
4
4
|
"description": "Provides theming support for the Wise Design system components",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"dist/",
|
|
44
44
|
"src/"
|
|
45
45
|
],
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "9ff5b52e35d76e7718fd67c8b0d59cb75885da12"
|
|
47
47
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
1
|
+
import { render, screen, act, waitFor } from '@testing-library/react';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
4
|
import { ThemeProvider } from './ThemeProvider';
|
|
5
|
+
|
|
5
6
|
import {} from './const';
|
|
6
7
|
|
|
7
8
|
const InValidElement = React.createContext<undefined>(undefined).Provider;
|
|
@@ -14,28 +15,6 @@ const ComponentWithClassname = ({
|
|
|
14
15
|
}) => <div className={className} {...rest} />;
|
|
15
16
|
|
|
16
17
|
describe('ThemeProvider', () => {
|
|
17
|
-
it('appends theme to children', () => {
|
|
18
|
-
render(
|
|
19
|
-
<ThemeProvider theme="light">
|
|
20
|
-
<div>children 1</div>
|
|
21
|
-
<div>children 2</div>
|
|
22
|
-
</ThemeProvider>,
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
expect(screen.getByText('children 1')).toHaveClass('np-theme-light');
|
|
26
|
-
expect(screen.getByText('children 2')).toHaveClass('np-theme-light');
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('concatenate className with children', () => {
|
|
30
|
-
render(
|
|
31
|
-
<ThemeProvider theme="light">
|
|
32
|
-
<div className="other class names">children</div>
|
|
33
|
-
</ThemeProvider>,
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
expect(screen.getByText('children')).toHaveClass('np-theme-light other class names');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
18
|
it('can nest theme providers', () => {
|
|
40
19
|
render(
|
|
41
20
|
<ThemeProvider theme="light">
|
|
@@ -49,37 +28,29 @@ describe('ThemeProvider', () => {
|
|
|
49
28
|
</ThemeProvider>,
|
|
50
29
|
);
|
|
51
30
|
|
|
52
|
-
|
|
53
|
-
expect(screen.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<ComponentWithClassname data-testid="button">Click me!</ComponentWithClassname>
|
|
61
|
-
</ThemeProvider>,
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
expect(screen.getByTestId('button')).toHaveClass('np-theme-personal');
|
|
31
|
+
// The first provider will not have a className attached to it
|
|
32
|
+
expect(screen.getAllByText('light')[0]).not.toHaveClass('np-theme-light');
|
|
33
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
34
|
+
expect(screen.getAllByText('light')[0].parentElement).not.toHaveClass('np-theme-light');
|
|
35
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
36
|
+
expect(screen.getByText('personal').parentElement).toHaveClass('np-theme-personal');
|
|
37
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
38
|
+
expect(screen.getAllByText('light')[1].parentElement).toHaveClass('np-theme-light');
|
|
65
39
|
});
|
|
66
40
|
|
|
67
|
-
it('
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
41
|
+
it('matches snapshot', () => {
|
|
42
|
+
const { asFragment } = render(
|
|
43
|
+
<ThemeProvider theme="light">
|
|
44
|
+
<div>light</div>
|
|
45
|
+
<ThemeProvider theme="personal">
|
|
46
|
+
<div>personal</div>
|
|
47
|
+
<ThemeProvider theme="light">
|
|
48
|
+
<div>light</div>
|
|
49
|
+
</ThemeProvider>
|
|
50
|
+
</ThemeProvider>
|
|
76
51
|
</ThemeProvider>,
|
|
77
52
|
);
|
|
78
53
|
|
|
79
|
-
expect(
|
|
80
|
-
// eslint-disable-next-line no-console
|
|
81
|
-
expect(console.warn).toHaveBeenCalledWith(
|
|
82
|
-
'[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!',
|
|
83
|
-
);
|
|
54
|
+
expect(asFragment()).toMatchSnapshot();
|
|
84
55
|
});
|
|
85
56
|
});
|
package/src/ThemeProvider.tsx
CHANGED
|
@@ -1,37 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createContext, PropsWithChildren, useContext } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { ThemeScript } from './ThemeScript';
|
|
4
|
+
import { ThemedChildren } from './ThemedChildren';
|
|
5
|
+
import { ComponentTheme, DEFAULT_COMPONENT_THEME, Theming } from './const';
|
|
5
6
|
|
|
6
|
-
export const ThemeContext = createContext<ComponentTheme>(
|
|
7
|
+
export const ThemeContext = createContext<ComponentTheme | undefined>(undefined);
|
|
7
8
|
|
|
8
|
-
type ThemeProviderProps = PropsWithChildren<
|
|
9
|
-
theme: ComponentTheme | undefined;
|
|
10
|
-
}>;
|
|
9
|
+
type ThemeProviderProps = PropsWithChildren<Theming>;
|
|
11
10
|
|
|
12
11
|
export const ThemeProvider = ({
|
|
13
12
|
theme = DEFAULT_COMPONENT_THEME,
|
|
14
13
|
children,
|
|
15
14
|
}: ThemeProviderProps) => {
|
|
16
|
-
const
|
|
17
|
-
if (!isValidElement(child)) {
|
|
18
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
19
|
-
// eslint-disable-next-line no-console
|
|
20
|
-
console.warn(
|
|
21
|
-
'[ThemeProvider] Trying to inject `className` to an invalid React element, this will be skipped!',
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
return child;
|
|
25
|
-
}
|
|
15
|
+
const isRootProvider = useContext(ThemeContext) === undefined;
|
|
26
16
|
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
if (isRootProvider) {
|
|
18
|
+
return (
|
|
19
|
+
<ThemeContext.Provider value={theme}>
|
|
20
|
+
<ThemeScript />
|
|
21
|
+
{children}
|
|
22
|
+
</ThemeContext.Provider>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return <ThemeContext.Provider value={theme}>{themedChildren}</ThemeContext.Provider>;
|
|
26
|
+
return (
|
|
27
|
+
<ThemeContext.Provider value={theme}>
|
|
28
|
+
<ThemedChildren>{children}</ThemedChildren>
|
|
29
|
+
</ThemeContext.Provider>
|
|
30
|
+
);
|
|
37
31
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { memo, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useTheme } from './useTheme';
|
|
4
|
+
|
|
5
|
+
export const ThemeScript = memo(
|
|
6
|
+
() => {
|
|
7
|
+
const { className } = useTheme();
|
|
8
|
+
|
|
9
|
+
const scriptSource = useMemo(() => {
|
|
10
|
+
return `!(function () {try {document.documentElement.classList.add('${className}');} catch (e) {}})();`;
|
|
11
|
+
}, [className]);
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line react/no-danger
|
|
14
|
+
return <script dangerouslySetInnerHTML={{ __html: scriptSource }} />;
|
|
15
|
+
},
|
|
16
|
+
// Never re-render this component
|
|
17
|
+
() => true,
|
|
18
|
+
);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useTheme } from './useTheme';
|
|
4
|
+
|
|
5
|
+
type ThemedChildrenProps = { children: ReactNode };
|
|
6
|
+
|
|
7
|
+
export const ThemedChildren = ({ children }: ThemedChildrenProps) => {
|
|
8
|
+
const { className } = useTheme();
|
|
9
|
+
|
|
10
|
+
return <div className={className}>{children}</div>;
|
|
11
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`ThemeProvider matches snapshot 1`] = `
|
|
4
|
+
<DocumentFragment>
|
|
5
|
+
<script>
|
|
6
|
+
!(function () {try {document.documentElement.classList.add('np-theme-light');} catch (e) {}})();
|
|
7
|
+
</script>
|
|
8
|
+
<div>
|
|
9
|
+
light
|
|
10
|
+
</div>
|
|
11
|
+
<div
|
|
12
|
+
class="np-theme-personal"
|
|
13
|
+
>
|
|
14
|
+
<div>
|
|
15
|
+
personal
|
|
16
|
+
</div>
|
|
17
|
+
<div
|
|
18
|
+
class="np-theme-light"
|
|
19
|
+
>
|
|
20
|
+
<div>
|
|
21
|
+
light
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</DocumentFragment>
|
|
26
|
+
`;
|
package/src/const.ts
CHANGED
package/src/index.ts
CHANGED