Skip to content

Commit

Permalink
Support MUI Global style overrides gregnb/mui-datatables#1871
Browse files Browse the repository at this point in the history
  • Loading branch information
garronej committed Mar 19, 2022
1 parent 8622645 commit 2b608bc
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 28 deletions.
108 changes: 99 additions & 9 deletions src/makeStyles.tsx
Expand Up @@ -2,12 +2,13 @@
import { useMemo } from "react";
import { objectFromEntries } from "./tools/polyfills/Object.fromEntries";
import { objectKeys } from "./tools/objectKeys";
import type { CSSObject } from "./types";
import type { CSSObject, CSSInterpolation } from "./types";
import { useCssAndCx } from "./cssAndCx";
import { getDependencyArrayRef } from "./tools/getDependencyArrayRef";
import { typeGuard } from "./tools/typeGuard";
import { useTssEmotionCache } from "./cache";
import { assert } from "./tools/assert";
import { mergeClasses } from "./mergeClasses";

const getCounter = (() => {
let counter = 0;
Expand Down Expand Up @@ -53,14 +54,23 @@ export function createMakeStyles<Theme>(params: { useTheme: () => Theme }) {

const outerCounter = getCounter();

return function useStyles(params: Params) {
return function useStyles(
params: Params,
styleOverrides?: {
props: { classes?: Record<string, string> } & Record<
string,
unknown
>;
ownerState?: Record<string, unknown>;
},
) {
const theme = useTheme();

const { css, cx } = useCssAndCx();

const cache = useTssEmotionCache();

return useMemo(() => {
let classes = useMemo(() => {
const refClassesCache: Record<string, string> = {};

type RefClasses = Record<
Expand Down Expand Up @@ -123,13 +133,93 @@ export function createMakeStyles<Theme>(params: { useTheme: () => Theme }) {
refClassesCache[ruleName];
});

return {
classes,
theme,
css,
cx,
};
return classes;
}, [cache, css, cx, theme, getDependencyArrayRef(params)]);

const propsClasses = styleOverrides?.props.classes;
{
classes = useMemo(
() => mergeClasses(classes, propsClasses, cx),
[classes, getDependencyArrayRef(propsClasses), cx],
);
}

{
let cssObjectByRuleNameOrGetCssObjectByRuleName:
| Record<
string,
| CSSInterpolation
| ((params: {
ownerState: any;
theme: Theme;
}) => CSSInterpolation)
>
| undefined = undefined;

try {
cssObjectByRuleNameOrGetCssObjectByRuleName =
name !== undefined
? (theme as any).components?.[name]
?.styleOverrides
: undefined;

// eslint-disable-next-line no-empty
} catch {}

const themeClasses = useMemo(() => {
if (!cssObjectByRuleNameOrGetCssObjectByRuleName) {
return undefined;
}

const themeClasses: Record<string, string> = {};

for (const ruleName in cssObjectByRuleNameOrGetCssObjectByRuleName) {
const cssObjectOrGetCssObject =
cssObjectByRuleNameOrGetCssObjectByRuleName[
ruleName
];

if (!(cssObjectOrGetCssObject instanceof Object)) {
continue;
}

themeClasses[ruleName] = css(
typeof cssObjectOrGetCssObject === "function"
? cssObjectOrGetCssObject({
theme,
"ownerState":
styleOverrides?.ownerState,
...styleOverrides?.props,
})
: cssObjectOrGetCssObject,
);
}

return themeClasses;
}, [
cssObjectByRuleNameOrGetCssObjectByRuleName ===
undefined
? undefined
: JSON.stringify(
cssObjectByRuleNameOrGetCssObjectByRuleName,
),
getDependencyArrayRef(styleOverrides?.props),
getDependencyArrayRef(styleOverrides?.ownerState),
css,
]);

classes = useMemo(
() => mergeClasses(classes, themeClasses, cx),
[classes, themeClasses, cx],
);
}

return {
classes,
theme,
css,
cx,
};
};
};
}
Expand Down
8 changes: 4 additions & 4 deletions src/mergeClasses.ts
Expand Up @@ -3,6 +3,7 @@

import type { Cx } from "./types";
import { objectKeys } from "./tools/objectKeys";
import { getDependencyArrayRef } from "./tools/getDependencyArrayRef";
import { useCssAndCx } from "./cssAndCx";
import { useMemo } from "react";

Expand All @@ -12,9 +13,8 @@ export function mergeClasses<T extends string, U extends string>(
cx: Cx,
): Record<T, string> &
(string extends U ? {} : Partial<Record<Exclude<U, T>, string>>) {
//NOTE: We use !(not) to be resilient for when it is used in withStyle
//where classes fromFromProps could diverge from the canonical type...
if (!classesFromProps) {
//NOTE: We use this test to be resilient in case classesFromProps is not of the expected type.
if (!(classesFromProps instanceof Object)) {
return classesFromUseStyles as any;
}

Expand Down Expand Up @@ -54,6 +54,6 @@ export function useMergedClasses<T extends string>(

return useMemo(
() => mergeClasses(classes, classesOv, cx),
[classes, classesOv, cx],
[classes, getDependencyArrayRef(classesOv), cx],
);
}
61 changes: 60 additions & 1 deletion src/test/apps/spa/src/App.tsx
@@ -1,5 +1,5 @@

import { memo } from "react";
import { useReducer, memo } from "react";
import { makeStyles, withStyles } from "makeStyles";
import { GlobalStyles, useMergedClasses } from "tss-react";
import { styled } from "@mui/material";
Expand Down Expand Up @@ -158,6 +158,16 @@ export function App(props: { className?: string; }) {
<MyTestComponentForMergedClasses />
<TestCastingMuiTypographyStyleToCSSObject />
<TestPr54 />
<TestingStyleOverrides
className={css({ "backgroundColor": "white" })}
classes={{
"root": css({
"backgroundColor": "red",
"border": "1px solid black"
})
}}
lightBulbBorderColor="black"
/>
</div>
</>
);
Expand Down Expand Up @@ -456,3 +466,52 @@ const { TestPr54 } = (() => {
return { TestPr54 };

})();

const { TestingStyleOverrides } = (() => {

type Props = {
className?: string;
classes?: Partial<ReturnType<typeof useStyles>["classes"]>;
lightBulbBorderColor: string;
}

function TestingStyleOverrides(props: Props) {

const { className } = props;

const [isOn, toggleIsOn] = useReducer(isOn => !isOn, false);

const { classes, cx } = useStyles(undefined, { props, "ownerState": { isOn } });

return (
<div className={cx(classes.root, className)} >
<div className={classes.lightBulb}></div>
<button onClick={toggleIsOn}>{`Turn ${isOn ? "off" : "on"}`}</button>
<p>Div should have a border, background should be white</p>
<p>Light bulb should have black border, it should be yellow when turned on.</p>
</div>
);

}

const useStyles = makeStyles({ "name": { TestingStyleOverrides } })(theme => ({
"root": {
"border": "1px solid black",
"width": 500,
"height": 200,
"position": "relative",
"color": "black"
},
"lightBulb": {
"position": "absolute",
"width": 50,
"height": 50,
"top": 120,
"left": 500 / 2 - 50,
"borderRadius": "50%"
}
}));

return { TestingStyleOverrides };

})();
15 changes: 15 additions & 0 deletions src/test/apps/spa/src/index.tsx
Expand Up @@ -20,12 +20,27 @@ const theme = createTheme({
"palette": {
"primary": {
"main": "#32CD32" //Limegreen
},
"info": {
"main": "#ffff00" //Yellow
}
},
"typography": {
"subtitle2": {
"fontStyle": "italic"
}
},
"components": {
//@ts-ignore
"TestingStyleOverrides": {
"styleOverrides": {
"lightBulb": ({ theme, ownerState: { isOn }, lightBulbBorderColor }: any) => ({
"border": `1px solid ${lightBulbBorderColor}`,
"backgroundColor": isOn ? theme.palette.info.main : "grey"
})
}

}
}
});

Expand Down
15 changes: 15 additions & 0 deletions src/test/apps/ssr/pages/_app.tsx
Expand Up @@ -24,12 +24,27 @@ export function App({ Component, pageProps }: AppProps) {
"mode": darkModeActive ? "dark" : "light",
"primary": {
"main": "#32CD32" //Limegreen
},
"info": {
"main": "#ffff00" //Yellow
}
},
"typography": {
"subtitle2": {
"fontStyle": "italic"
}
},
"components": {
//@ts-ignore
"TestingStyleOverrides": {
"styleOverrides": {
"lightBulb": ({ theme, ownerState: { isOn }, lightBulbBorderColor }: any)=>({
"border": `1px solid ${lightBulbBorderColor}`,
"backgroundColor": isOn ? theme.palette.info.main : "grey"
})
}

}
}
}),
[darkModeActive]
Expand Down
64 changes: 62 additions & 2 deletions src/test/apps/ssr/pages/index.tsx
@@ -1,5 +1,5 @@
import Head from "next/head";
import { memo } from "react";

import { useReducer, memo } from "react";
import { GlobalStyles, useMergedClasses } from "tss-react";
import { makeStyles, useStyles, withStyles } from "../shared/makeStyles";
import { styled } from "@mui/material";
Expand Down Expand Up @@ -199,6 +199,16 @@ const { App } = (() => {
<MyTestComponentForMergedClasses />
<TestCastingMuiTypographyStyleToCSSObject />
<TestPr54 />
<TestingStyleOverrides
className={css({ "backgroundColor": "white" })}
classes={{
"root": css({
"backgroundColor": "red",
"border": "1px solid black"
})
}}
lightBulbBorderColor="black"
/>
</div>
</>
);
Expand Down Expand Up @@ -510,3 +520,53 @@ const { TestPr54 } = (() => {
return { TestPr54 };

})();


const { TestingStyleOverrides } = (() => {

type Props = {
className?: string;
classes?: Partial<ReturnType<typeof useStyles>["classes"]>;
lightBulbBorderColor: string;
}

function TestingStyleOverrides(props: Props) {

const { className } = props;

const [isOn, toggleIsOn] = useReducer(isOn => !isOn, false);

const { classes, cx } = useStyles(undefined, { props, "ownerState": { isOn } });

return (
<div className={cx(classes.root, className)} >
<div className={classes.lightBulb}></div>
<button onClick={toggleIsOn}>{`Turn ${isOn?"off":"on"}`}</button>
<p>Div should have a border, background should be white</p>
<p>Light bulb should have black border, it should be yellow when turned on.</p>
</div>
);

}

const useStyles = makeStyles({ "name": { TestingStyleOverrides } })(theme => ({
"root": {
"border": "1px solid black",
"width": 500,
"height": 200,
"position": "relative",
"color": "black"
},
"lightBulb": {
"position": "absolute",
"width": 50,
"height": 50,
"top": 120,
"left": 500/2 - 50,
"borderRadius": "50%"
}
}));

return { TestingStyleOverrides };

})();

0 comments on commit 2b608bc

Please sign in to comment.