@wise/components-theming 0.0.0-experimental-15bed93 → 0.0.0-experimental-9fc22a2
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/ThemeProvider.d.ts.map +1 -1
- package/dist/ThemeProvider.js +11 -9
- package/dist/ThemeProvider.js.map +1 -1
- package/dist/ThemeProvider.mjs +11 -9
- package/dist/ThemeProvider.mjs.map +1 -1
- package/package.json +1 -1
- package/src/ThemeProvider.spec.tsx +134 -0
- package/src/ThemeProvider.tsx +13 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.d.ts","sourceRoot":"","sources":["../src/ThemeProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAkC,MAAM,OAAO,CAAC;AAG1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAKvC,KAAK,kBAAkB,GAAG,iBAAiB,CAAC,OAAO,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAK9E,eAAO,MAAM,aAAa,GAAI,gEAM3B,kBAAkB,
|
|
1
|
+
{"version":3,"file":"ThemeProvider.d.ts","sourceRoot":"","sources":["../src/ThemeProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAkC,MAAM,OAAO,CAAC;AAG1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAKvC,KAAK,kBAAkB,GAAG,iBAAiB,CAAC,OAAO,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAK9E,eAAO,MAAM,aAAa,GAAI,gEAM3B,kBAAkB,gCAqCpB,CAAC"}
|
package/dist/ThemeProvider.js
CHANGED
|
@@ -13,29 +13,31 @@ const ThemeProvider = ({
|
|
|
13
13
|
screenMode = _const.DEFAULT_SCREEN_MODE,
|
|
14
14
|
children,
|
|
15
15
|
className = undefined,
|
|
16
|
-
isNotRootProvider =
|
|
16
|
+
isNotRootProvider = false
|
|
17
17
|
}) => {
|
|
18
|
-
const
|
|
19
|
-
const isContextRoot = isNotRootProvider !== undefined ? !isNotRootProvider : !contextExists;
|
|
18
|
+
const isContextRoot = react.useContext(ThemeProviderContext.ThemeContext) === undefined;
|
|
20
19
|
// useEffect hook used to apply the theme class to the HTML element
|
|
21
20
|
react.useEffect(() => {
|
|
22
|
-
if (isContextRoot) {
|
|
21
|
+
if (!isNotRootProvider && isContextRoot) {
|
|
23
22
|
// Remove all the theme classes from the documentElement
|
|
24
23
|
document.documentElement.className.match(themeClass)?.forEach(item => {
|
|
25
24
|
document.documentElement.classList.remove(item);
|
|
26
25
|
});
|
|
27
26
|
const themeClasses = helpers.getThemeClassName(theme, screenMode).split(' ');
|
|
27
|
+
// Auto-detection: Add np-theme-personal for business and platform themes when this is a root provider (page wrapper)
|
|
28
|
+
const isBusinessTheme = theme === 'business' || theme?.startsWith('business');
|
|
29
|
+
const isPlatformTheme = theme === 'platform' || theme?.startsWith('platform');
|
|
30
|
+
const shouldAddPersonalClass = (isBusinessTheme || isPlatformTheme) && !isNotRootProvider;
|
|
31
|
+
// Apply theme classes
|
|
28
32
|
themeClasses.forEach(item => {
|
|
29
33
|
document.documentElement.classList.add(item);
|
|
30
34
|
});
|
|
31
|
-
// Add np-theme-personal for business and platform themes
|
|
32
|
-
|
|
33
|
-
const isPlatformTheme = theme === 'platform' || theme?.startsWith('platform');
|
|
34
|
-
if (isBusinessTheme || isPlatformTheme) {
|
|
35
|
+
// Add np-theme-personal for business and platform themes only in root provider (page wrapper)
|
|
36
|
+
if (shouldAddPersonalClass) {
|
|
35
37
|
document.documentElement.classList.add('np-theme-personal');
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
|
-
}, [isContextRoot, theme, screenMode]);
|
|
40
|
+
}, [isNotRootProvider, isContextRoot, theme, screenMode]);
|
|
39
41
|
const contextValue = react.useMemo(() => ({
|
|
40
42
|
theme,
|
|
41
43
|
screenMode
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.js","sources":["../src/ThemeProvider.tsx"],"sourcesContent":["import { PropsWithChildren, useContext, useEffect, useMemo } from 'react';\n\nimport { ThemedChildren } from './ThemedChildren';\nimport type { Theming } from './const';\nimport { DEFAULT_BASE_THEME, DEFAULT_SCREEN_MODE } from './const';\nimport { getThemeClassName } from './helpers';\nimport { ThemeContext } from './ThemeProviderContext';\n\ntype ThemeProviderProps = PropsWithChildren<Theming> & { className?: string };\n\n// RegEx to check for `np-theme-` class name\nconst themeClass = /\\bnp-theme-[a-z-]+\\b/g;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_BASE_THEME,\n screenMode = DEFAULT_SCREEN_MODE,\n children,\n className = undefined,\n isNotRootProvider =
|
|
1
|
+
{"version":3,"file":"ThemeProvider.js","sources":["../src/ThemeProvider.tsx"],"sourcesContent":["import { PropsWithChildren, useContext, useEffect, useMemo } from 'react';\n\nimport { ThemedChildren } from './ThemedChildren';\nimport type { Theming } from './const';\nimport { DEFAULT_BASE_THEME, DEFAULT_SCREEN_MODE } from './const';\nimport { getThemeClassName } from './helpers';\nimport { ThemeContext } from './ThemeProviderContext';\n\ntype ThemeProviderProps = PropsWithChildren<Theming> & { className?: string };\n\n// RegEx to check for `np-theme-` class name\nconst themeClass = /\\bnp-theme-[a-z-]+\\b/g;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_BASE_THEME,\n screenMode = DEFAULT_SCREEN_MODE,\n children,\n className = undefined,\n isNotRootProvider = false,\n}: ThemeProviderProps) => {\n const isContextRoot = useContext(ThemeContext) === undefined;\n\n // useEffect hook used to apply the theme class to the HTML element\n useEffect(() => {\n if (!isNotRootProvider && isContextRoot) {\n // Remove all the theme classes from the documentElement\n document.documentElement.className.match(themeClass)?.forEach((item) => {\n document.documentElement.classList.remove(item);\n });\n\n const themeClasses = getThemeClassName(theme, screenMode).split(' ');\n\n // Auto-detection: Add np-theme-personal for business and platform themes when this is a root provider (page wrapper)\n const isBusinessTheme = theme === 'business' || theme?.startsWith('business');\n const isPlatformTheme = theme === 'platform' || theme?.startsWith('platform');\n const shouldAddPersonalClass = (isBusinessTheme || isPlatformTheme) && !isNotRootProvider;\n\n // Apply theme classes\n themeClasses.forEach((item) => {\n document.documentElement.classList.add(item);\n });\n\n // Add np-theme-personal for business and platform themes only in root provider (page wrapper)\n if (shouldAddPersonalClass) {\n document.documentElement.classList.add('np-theme-personal');\n }\n }\n }, [isNotRootProvider, isContextRoot, theme, screenMode]);\n\n const contextValue = useMemo(() => ({ theme, screenMode }), [screenMode, theme]);\n\n return (\n <ThemeContext.Provider value={contextValue}>\n <ThemedChildren className={className}>{children}</ThemedChildren>\n </ThemeContext.Provider>\n );\n};\n"],"names":["themeClass","ThemeProvider","theme","DEFAULT_BASE_THEME","screenMode","DEFAULT_SCREEN_MODE","children","className","undefined","isNotRootProvider","isContextRoot","useContext","ThemeContext","useEffect","document","documentElement","match","forEach","item","classList","remove","themeClasses","getThemeClassName","split","isBusinessTheme","startsWith","isPlatformTheme","shouldAddPersonalClass","add","contextValue","useMemo","_jsx","Provider","value","ThemedChildren"],"mappings":";;;;;;;;;AAWA,MAAMA,UAAU,GAAG,uBAAuB;AAEnC,MAAMC,aAAa,GAAGA,CAAC;AAC5BC,EAAAA,KAAK,GAAGC,yBAAkB;AAC1BC,EAAAA,UAAU,GAAGC,0BAAmB;EAChCC,QAAQ;AACRC,EAAAA,SAAS,GAAGC,SAAS;AACrBC,EAAAA,iBAAiB,GAAG;AAAK,CACN,KAAI;AACvB,EAAA,MAAMC,aAAa,GAAGC,gBAAU,CAACC,iCAAY,CAAC,KAAKJ,SAAS;AAE5D;AACAK,EAAAA,eAAS,CAAC,MAAK;AACb,IAAA,IAAI,CAACJ,iBAAiB,IAAIC,aAAa,EAAE;AACvC;AACAI,MAAAA,QAAQ,CAACC,eAAe,CAACR,SAAS,CAACS,KAAK,CAAChB,UAAU,CAAC,EAAEiB,OAAO,CAAEC,IAAI,IAAI;QACrEJ,QAAQ,CAACC,eAAe,CAACI,SAAS,CAACC,MAAM,CAACF,IAAI,CAAC;AACjD,MAAA,CAAC,CAAC;AAEF,MAAA,MAAMG,YAAY,GAAGC,yBAAiB,CAACpB,KAAK,EAAEE,UAAU,CAAC,CAACmB,KAAK,CAAC,GAAG,CAAC;AAEpE;MACA,MAAMC,eAAe,GAAGtB,KAAK,KAAK,UAAU,IAAIA,KAAK,EAAEuB,UAAU,CAAC,UAAU,CAAC;MAC7E,MAAMC,eAAe,GAAGxB,KAAK,KAAK,UAAU,IAAIA,KAAK,EAAEuB,UAAU,CAAC,UAAU,CAAC;MAC7E,MAAME,sBAAsB,GAAG,CAACH,eAAe,IAAIE,eAAe,KAAK,CAACjB,iBAAiB;AAEzF;AACAY,MAAAA,YAAY,CAACJ,OAAO,CAAEC,IAAI,IAAI;QAC5BJ,QAAQ,CAACC,eAAe,CAACI,SAAS,CAACS,GAAG,CAACV,IAAI,CAAC;AAC9C,MAAA,CAAC,CAAC;AAEF;AACA,MAAA,IAAIS,sBAAsB,EAAE;QAC1Bb,QAAQ,CAACC,eAAe,CAACI,SAAS,CAACS,GAAG,CAAC,mBAAmB,CAAC;AAC7D,MAAA;AACF,IAAA;EACF,CAAC,EAAE,CAACnB,iBAAiB,EAAEC,aAAa,EAAER,KAAK,EAAEE,UAAU,CAAC,CAAC;AAEzD,EAAA,MAAMyB,YAAY,GAAGC,aAAO,CAAC,OAAO;IAAE5B,KAAK;AAAEE,IAAAA;AAAU,GAAE,CAAC,EAAE,CAACA,UAAU,EAAEF,KAAK,CAAC,CAAC;AAEhF,EAAA,oBACE6B,cAAA,CAACnB,iCAAY,CAACoB,QAAQ,EAAA;AAACC,IAAAA,KAAK,EAAEJ,YAAa;IAAAvB,QAAA,eACzCyB,cAAA,CAACG,6BAAc,EAAA;AAAC3B,MAAAA,SAAS,EAAEA,SAAU;AAAAD,MAAAA,QAAA,EAAEA;KAAyB;AAClE,GAAuB,CAAC;AAE5B;;;;"}
|
package/dist/ThemeProvider.mjs
CHANGED
|
@@ -11,29 +11,31 @@ const ThemeProvider = ({
|
|
|
11
11
|
screenMode = DEFAULT_SCREEN_MODE,
|
|
12
12
|
children,
|
|
13
13
|
className = undefined,
|
|
14
|
-
isNotRootProvider =
|
|
14
|
+
isNotRootProvider = false
|
|
15
15
|
}) => {
|
|
16
|
-
const
|
|
17
|
-
const isContextRoot = isNotRootProvider !== undefined ? !isNotRootProvider : !contextExists;
|
|
16
|
+
const isContextRoot = useContext(ThemeContext) === undefined;
|
|
18
17
|
// useEffect hook used to apply the theme class to the HTML element
|
|
19
18
|
useEffect(() => {
|
|
20
|
-
if (isContextRoot) {
|
|
19
|
+
if (!isNotRootProvider && isContextRoot) {
|
|
21
20
|
// Remove all the theme classes from the documentElement
|
|
22
21
|
document.documentElement.className.match(themeClass)?.forEach(item => {
|
|
23
22
|
document.documentElement.classList.remove(item);
|
|
24
23
|
});
|
|
25
24
|
const themeClasses = getThemeClassName(theme, screenMode).split(' ');
|
|
25
|
+
// Auto-detection: Add np-theme-personal for business and platform themes when this is a root provider (page wrapper)
|
|
26
|
+
const isBusinessTheme = theme === 'business' || theme?.startsWith('business');
|
|
27
|
+
const isPlatformTheme = theme === 'platform' || theme?.startsWith('platform');
|
|
28
|
+
const shouldAddPersonalClass = (isBusinessTheme || isPlatformTheme) && !isNotRootProvider;
|
|
29
|
+
// Apply theme classes
|
|
26
30
|
themeClasses.forEach(item => {
|
|
27
31
|
document.documentElement.classList.add(item);
|
|
28
32
|
});
|
|
29
|
-
// Add np-theme-personal for business and platform themes
|
|
30
|
-
|
|
31
|
-
const isPlatformTheme = theme === 'platform' || theme?.startsWith('platform');
|
|
32
|
-
if (isBusinessTheme || isPlatformTheme) {
|
|
33
|
+
// Add np-theme-personal for business and platform themes only in root provider (page wrapper)
|
|
34
|
+
if (shouldAddPersonalClass) {
|
|
33
35
|
document.documentElement.classList.add('np-theme-personal');
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
|
-
}, [isContextRoot, theme, screenMode]);
|
|
38
|
+
}, [isNotRootProvider, isContextRoot, theme, screenMode]);
|
|
37
39
|
const contextValue = useMemo(() => ({
|
|
38
40
|
theme,
|
|
39
41
|
screenMode
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.mjs","sources":["../src/ThemeProvider.tsx"],"sourcesContent":["import { PropsWithChildren, useContext, useEffect, useMemo } from 'react';\n\nimport { ThemedChildren } from './ThemedChildren';\nimport type { Theming } from './const';\nimport { DEFAULT_BASE_THEME, DEFAULT_SCREEN_MODE } from './const';\nimport { getThemeClassName } from './helpers';\nimport { ThemeContext } from './ThemeProviderContext';\n\ntype ThemeProviderProps = PropsWithChildren<Theming> & { className?: string };\n\n// RegEx to check for `np-theme-` class name\nconst themeClass = /\\bnp-theme-[a-z-]+\\b/g;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_BASE_THEME,\n screenMode = DEFAULT_SCREEN_MODE,\n children,\n className = undefined,\n isNotRootProvider =
|
|
1
|
+
{"version":3,"file":"ThemeProvider.mjs","sources":["../src/ThemeProvider.tsx"],"sourcesContent":["import { PropsWithChildren, useContext, useEffect, useMemo } from 'react';\n\nimport { ThemedChildren } from './ThemedChildren';\nimport type { Theming } from './const';\nimport { DEFAULT_BASE_THEME, DEFAULT_SCREEN_MODE } from './const';\nimport { getThemeClassName } from './helpers';\nimport { ThemeContext } from './ThemeProviderContext';\n\ntype ThemeProviderProps = PropsWithChildren<Theming> & { className?: string };\n\n// RegEx to check for `np-theme-` class name\nconst themeClass = /\\bnp-theme-[a-z-]+\\b/g;\n\nexport const ThemeProvider = ({\n theme = DEFAULT_BASE_THEME,\n screenMode = DEFAULT_SCREEN_MODE,\n children,\n className = undefined,\n isNotRootProvider = false,\n}: ThemeProviderProps) => {\n const isContextRoot = useContext(ThemeContext) === undefined;\n\n // useEffect hook used to apply the theme class to the HTML element\n useEffect(() => {\n if (!isNotRootProvider && isContextRoot) {\n // Remove all the theme classes from the documentElement\n document.documentElement.className.match(themeClass)?.forEach((item) => {\n document.documentElement.classList.remove(item);\n });\n\n const themeClasses = getThemeClassName(theme, screenMode).split(' ');\n\n // Auto-detection: Add np-theme-personal for business and platform themes when this is a root provider (page wrapper)\n const isBusinessTheme = theme === 'business' || theme?.startsWith('business');\n const isPlatformTheme = theme === 'platform' || theme?.startsWith('platform');\n const shouldAddPersonalClass = (isBusinessTheme || isPlatformTheme) && !isNotRootProvider;\n\n // Apply theme classes\n themeClasses.forEach((item) => {\n document.documentElement.classList.add(item);\n });\n\n // Add np-theme-personal for business and platform themes only in root provider (page wrapper)\n if (shouldAddPersonalClass) {\n document.documentElement.classList.add('np-theme-personal');\n }\n }\n }, [isNotRootProvider, isContextRoot, theme, screenMode]);\n\n const contextValue = useMemo(() => ({ theme, screenMode }), [screenMode, theme]);\n\n return (\n <ThemeContext.Provider value={contextValue}>\n <ThemedChildren className={className}>{children}</ThemedChildren>\n </ThemeContext.Provider>\n );\n};\n"],"names":["themeClass","ThemeProvider","theme","DEFAULT_BASE_THEME","screenMode","DEFAULT_SCREEN_MODE","children","className","undefined","isNotRootProvider","isContextRoot","useContext","ThemeContext","useEffect","document","documentElement","match","forEach","item","classList","remove","themeClasses","getThemeClassName","split","isBusinessTheme","startsWith","isPlatformTheme","shouldAddPersonalClass","add","contextValue","useMemo","_jsx","Provider","value","ThemedChildren"],"mappings":";;;;;;;AAWA,MAAMA,UAAU,GAAG,uBAAuB;AAEnC,MAAMC,aAAa,GAAGA,CAAC;AAC5BC,EAAAA,KAAK,GAAGC,kBAAkB;AAC1BC,EAAAA,UAAU,GAAGC,mBAAmB;EAChCC,QAAQ;AACRC,EAAAA,SAAS,GAAGC,SAAS;AACrBC,EAAAA,iBAAiB,GAAG;AAAK,CACN,KAAI;AACvB,EAAA,MAAMC,aAAa,GAAGC,UAAU,CAACC,YAAY,CAAC,KAAKJ,SAAS;AAE5D;AACAK,EAAAA,SAAS,CAAC,MAAK;AACb,IAAA,IAAI,CAACJ,iBAAiB,IAAIC,aAAa,EAAE;AACvC;AACAI,MAAAA,QAAQ,CAACC,eAAe,CAACR,SAAS,CAACS,KAAK,CAAChB,UAAU,CAAC,EAAEiB,OAAO,CAAEC,IAAI,IAAI;QACrEJ,QAAQ,CAACC,eAAe,CAACI,SAAS,CAACC,MAAM,CAACF,IAAI,CAAC;AACjD,MAAA,CAAC,CAAC;AAEF,MAAA,MAAMG,YAAY,GAAGC,iBAAiB,CAACpB,KAAK,EAAEE,UAAU,CAAC,CAACmB,KAAK,CAAC,GAAG,CAAC;AAEpE;MACA,MAAMC,eAAe,GAAGtB,KAAK,KAAK,UAAU,IAAIA,KAAK,EAAEuB,UAAU,CAAC,UAAU,CAAC;MAC7E,MAAMC,eAAe,GAAGxB,KAAK,KAAK,UAAU,IAAIA,KAAK,EAAEuB,UAAU,CAAC,UAAU,CAAC;MAC7E,MAAME,sBAAsB,GAAG,CAACH,eAAe,IAAIE,eAAe,KAAK,CAACjB,iBAAiB;AAEzF;AACAY,MAAAA,YAAY,CAACJ,OAAO,CAAEC,IAAI,IAAI;QAC5BJ,QAAQ,CAACC,eAAe,CAACI,SAAS,CAACS,GAAG,CAACV,IAAI,CAAC;AAC9C,MAAA,CAAC,CAAC;AAEF;AACA,MAAA,IAAIS,sBAAsB,EAAE;QAC1Bb,QAAQ,CAACC,eAAe,CAACI,SAAS,CAACS,GAAG,CAAC,mBAAmB,CAAC;AAC7D,MAAA;AACF,IAAA;EACF,CAAC,EAAE,CAACnB,iBAAiB,EAAEC,aAAa,EAAER,KAAK,EAAEE,UAAU,CAAC,CAAC;AAEzD,EAAA,MAAMyB,YAAY,GAAGC,OAAO,CAAC,OAAO;IAAE5B,KAAK;AAAEE,IAAAA;AAAU,GAAE,CAAC,EAAE,CAACA,UAAU,EAAEF,KAAK,CAAC,CAAC;AAEhF,EAAA,oBACE6B,GAAA,CAACnB,YAAY,CAACoB,QAAQ,EAAA;AAACC,IAAAA,KAAK,EAAEJ,YAAa;IAAAvB,QAAA,eACzCyB,GAAA,CAACG,cAAc,EAAA;AAAC3B,MAAAA,SAAS,EAAEA,SAAU;AAAAD,MAAAA,QAAA,EAAEA;KAAyB;AAClE,GAAuB,CAAC;AAE5B;;;;"}
|
package/package.json
CHANGED
|
@@ -77,6 +77,140 @@ describe('ThemeProvider', () => {
|
|
|
77
77
|
);
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
it('adds personal class for business theme when isNotRootProvider=false (default)', () => {
|
|
81
|
+
const mockClassList = {
|
|
82
|
+
add: jest.fn(),
|
|
83
|
+
remove: jest.fn(),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
Object.defineProperty(document, 'documentElement', {
|
|
87
|
+
value: { className: '', classList: mockClassList },
|
|
88
|
+
writable: true,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
render(
|
|
92
|
+
<ThemeProvider theme="business">
|
|
93
|
+
<div>business-auto-fix</div>
|
|
94
|
+
</ThemeProvider>,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(mockClassList.add).toHaveBeenCalledWith('np-theme-personal');
|
|
98
|
+
expect(mockClassList.add).toHaveBeenCalledWith('np-theme-business');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('does not add personal class for business theme with isNotRootProvider=true', () => {
|
|
102
|
+
const mockClassList = {
|
|
103
|
+
add: jest.fn(),
|
|
104
|
+
remove: jest.fn(),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
Object.defineProperty(document, 'documentElement', {
|
|
108
|
+
value: { className: '', classList: mockClassList },
|
|
109
|
+
writable: true,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
render(
|
|
113
|
+
<ThemeProvider theme="business" isNotRootProvider>
|
|
114
|
+
<div>business-component</div>
|
|
115
|
+
</ThemeProvider>,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(mockClassList.add).not.toHaveBeenCalledWith('np-theme-personal');
|
|
119
|
+
expect(mockClassList.add).not.toHaveBeenCalled();
|
|
120
|
+
|
|
121
|
+
// The component wrapper should still get the business theme class via ThemedChildren
|
|
122
|
+
expect(screen.getByText('business-component')).toBeInTheDocument();
|
|
123
|
+
// Note: We can't easily test the wrapper class without direct node access,
|
|
124
|
+
// but the functionality is verified by the existing integration tests
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('adds personal class for personal theme', () => {
|
|
128
|
+
const mockClassList = {
|
|
129
|
+
add: jest.fn(),
|
|
130
|
+
remove: jest.fn(),
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
Object.defineProperty(document, 'documentElement', {
|
|
134
|
+
value: { className: '', classList: mockClassList },
|
|
135
|
+
writable: true,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
render(
|
|
139
|
+
<ThemeProvider theme="personal">
|
|
140
|
+
<div>personal</div>
|
|
141
|
+
</ThemeProvider>,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(mockClassList.add).toHaveBeenCalledWith('np-theme-personal');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('does not add personal class for light theme', () => {
|
|
148
|
+
const mockClassList = {
|
|
149
|
+
add: jest.fn(),
|
|
150
|
+
remove: jest.fn(),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
Object.defineProperty(document, 'documentElement', {
|
|
154
|
+
value: { className: '', classList: mockClassList },
|
|
155
|
+
writable: true,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
render(
|
|
159
|
+
<ThemeProvider theme="light">
|
|
160
|
+
<div>light</div>
|
|
161
|
+
</ThemeProvider>,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(mockClassList.add).toHaveBeenCalledWith('np-theme-light');
|
|
165
|
+
expect(mockClassList.add).not.toHaveBeenCalledWith('np-theme-personal');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('adds personal class for platform theme when isNotRootProvider=false (default)', () => {
|
|
169
|
+
const mockClassList = {
|
|
170
|
+
add: jest.fn(),
|
|
171
|
+
remove: jest.fn(),
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
Object.defineProperty(document, 'documentElement', {
|
|
175
|
+
value: { className: '', classList: mockClassList },
|
|
176
|
+
writable: true,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
render(
|
|
180
|
+
<ThemeProvider theme="platform">
|
|
181
|
+
<div>platform-auto-fix</div>
|
|
182
|
+
</ThemeProvider>,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
expect(mockClassList.add).toHaveBeenCalledWith('np-theme-personal');
|
|
186
|
+
expect(mockClassList.add).toHaveBeenCalledWith('np-theme-platform');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('does not add personal class for platform theme with isNotRootProvider=true', () => {
|
|
190
|
+
const mockClassList = {
|
|
191
|
+
add: jest.fn(),
|
|
192
|
+
remove: jest.fn(),
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
Object.defineProperty(document, 'documentElement', {
|
|
196
|
+
value: { className: '', classList: mockClassList },
|
|
197
|
+
writable: true,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
render(
|
|
201
|
+
<ThemeProvider theme="platform" isNotRootProvider>
|
|
202
|
+
<div>platform-component</div>
|
|
203
|
+
</ThemeProvider>,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
expect(mockClassList.add).not.toHaveBeenCalledWith('np-theme-personal');
|
|
207
|
+
expect(mockClassList.add).not.toHaveBeenCalled();
|
|
208
|
+
|
|
209
|
+
// The component wrapper should still get the platform theme class via ThemedChildren
|
|
210
|
+
expect(screen.getByText('platform-component')).toBeInTheDocument();
|
|
211
|
+
// Note: We can't easily test the wrapper class without direct node access,
|
|
212
|
+
// but the functionality is verified by the existing integration tests
|
|
213
|
+
});
|
|
80
214
|
it('matches snapshot', () => {
|
|
81
215
|
const { asFragment } = render(
|
|
82
216
|
<ThemeProvider theme="light">
|
package/src/ThemeProvider.tsx
CHANGED
|
@@ -16,33 +16,36 @@ export const ThemeProvider = ({
|
|
|
16
16
|
screenMode = DEFAULT_SCREEN_MODE,
|
|
17
17
|
children,
|
|
18
18
|
className = undefined,
|
|
19
|
-
isNotRootProvider =
|
|
19
|
+
isNotRootProvider = false,
|
|
20
20
|
}: ThemeProviderProps) => {
|
|
21
|
-
const
|
|
22
|
-
const isContextRoot = isNotRootProvider !== undefined ? !isNotRootProvider : !contextExists;
|
|
21
|
+
const isContextRoot = useContext(ThemeContext) === undefined;
|
|
23
22
|
|
|
24
23
|
// useEffect hook used to apply the theme class to the HTML element
|
|
25
24
|
useEffect(() => {
|
|
26
|
-
if (isContextRoot) {
|
|
25
|
+
if (!isNotRootProvider && isContextRoot) {
|
|
27
26
|
// Remove all the theme classes from the documentElement
|
|
28
27
|
document.documentElement.className.match(themeClass)?.forEach((item) => {
|
|
29
28
|
document.documentElement.classList.remove(item);
|
|
30
29
|
});
|
|
31
30
|
|
|
32
31
|
const themeClasses = getThemeClassName(theme, screenMode).split(' ');
|
|
33
|
-
themeClasses.forEach((item) => {
|
|
34
|
-
document.documentElement.classList.add(item);
|
|
35
|
-
});
|
|
36
32
|
|
|
37
|
-
// Add np-theme-personal for business and platform themes
|
|
33
|
+
// Auto-detection: Add np-theme-personal for business and platform themes when this is a root provider (page wrapper)
|
|
38
34
|
const isBusinessTheme = theme === 'business' || theme?.startsWith('business');
|
|
39
35
|
const isPlatformTheme = theme === 'platform' || theme?.startsWith('platform');
|
|
36
|
+
const shouldAddPersonalClass = (isBusinessTheme || isPlatformTheme) && !isNotRootProvider;
|
|
37
|
+
|
|
38
|
+
// Apply theme classes
|
|
39
|
+
themeClasses.forEach((item) => {
|
|
40
|
+
document.documentElement.classList.add(item);
|
|
41
|
+
});
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
// Add np-theme-personal for business and platform themes only in root provider (page wrapper)
|
|
44
|
+
if (shouldAddPersonalClass) {
|
|
42
45
|
document.documentElement.classList.add('np-theme-personal');
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
|
-
}, [isContextRoot, theme, screenMode]);
|
|
48
|
+
}, [isNotRootProvider, isContextRoot, theme, screenMode]);
|
|
46
49
|
|
|
47
50
|
const contextValue = useMemo(() => ({ theme, screenMode }), [screenMode, theme]);
|
|
48
51
|
|