Skip to content

Commit

Permalink
add arrays support
Browse files Browse the repository at this point in the history
  • Loading branch information
mnajdova committed Sep 22, 2023
1 parent f10f4e1 commit 0414435
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 102 deletions.
171 changes: 120 additions & 51 deletions packages/mui-system/src/createStyled.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
/* eslint-disable no-underscore-dangle */
import styledEngineStyled, { internal_processStyles as processStyles } from '@mui/styled-engine';
import { getDisplayName, unstable_capitalize as capitalize, isPlainObject, deepmerge } from '@mui/utils';
import {
getDisplayName,
unstable_capitalize as capitalize,
isPlainObject,
deepmerge,
} from '@mui/utils';
import createTheme from './createTheme';
import propsToClassKey from './propsToClassKey';
import styleFunctionSx from './styleFunctionSx';
Expand Down Expand Up @@ -39,7 +44,7 @@ const transformVariants = (variants) => {
}

return variantsStyles;
}
};
const getVariantStyles = (name, theme) => {
let variants = [];
if (theme && theme.components && theme.components[name] && theme.components[name].variants) {
Expand Down Expand Up @@ -173,32 +178,132 @@ export default function createStyled(input = {}) {
// On the server Emotion doesn't use React.forwardRef for creating components, so the created
// component stays as a function. This condition makes sure that we do not interpolate functions
// which are basically components used as a selectors.
return typeof stylesArg === 'function' && stylesArg.__emotion_real !== stylesArg
? (props) => {
return stylesArg({
...props,
theme: resolveTheme({ ...props, defaultTheme, themeId }),
if (typeof stylesArg === 'function' && stylesArg.__emotion_real !== stylesArg) {
return (props) => {
const resolvedStyles = stylesArg({
...props,
theme: resolveTheme({ ...props, defaultTheme, themeId }),
});

let optionalVariants;
if (isPlainObject(resolvedStyles)) {
if (resolvedStyles && resolvedStyles.variants) {
optionalVariants = resolvedStyles.variants;
}
delete resolvedStyles['variants'];
}
let result = resolvedStyles;

const variantsStyles = variantsResolver(
props,
transformVariants(optionalVariants),
optionalVariants,
);

// the variantsStyle is an array of all variant styles that need to be applied,
// so we need to merge them on top of the rest of the styles
if (variantsStyles) {
variantsStyles.forEach((variantStyle) => {
result = deepmerge(result, variantStyle);
});
}
: stylesArg;

return result;
};
} else if (isPlainObject(stylesArg)) {
let transformedStylesArg = stylesArg;
let styledArgVariants;
if (stylesArg && stylesArg.variants) {
styledArgVariants = stylesArg.variants;
}
delete transformedStylesArg['variants'];

if (styledArgVariants) {
transformedStylesArg = (props) => {
console.log(props);
let result = stylesArg;
const variantStyles = variantsResolver(
props,
transformVariants(styledArgVariants),
styledArgVariants,
);
variantStyles.forEach((variantStyle) => {
result = deepmerge(result, variantStyle);
});
console.log(result);

return result;
};
}
return transformedStylesArg;
}
return stylesArg;
})
: [];

let transformedStyleArg = styleArg;

let styledArgVariants;

if (isPlainObject(styleArg)) {
let styledArgVariants;
if (styleArg && styleArg.variants) {
styledArgVariants = styleArg.variants;
}
delete transformedStyleArg['variants'];
}

if (styledArgVariants) {
expressionsWithDefaultTheme.push((props) => {
return variantsResolver(props, transformVariants(styledArgVariants), styledArgVariants);
})
if (styledArgVariants) {
transformedStyleArg = (props) => {
let result = styleArg;
const variantStyles = variantsResolver(
props,
transformVariants(styledArgVariants),
styledArgVariants,
);
variantStyles.forEach((variantStyle) => {
result = deepmerge(result, variantStyle);
});

return result;
};
}
} else if (
typeof styleArg === 'function' &&
// On the server Emotion doesn't use React.forwardRef for creating components, so the created
// component stays as a function. This condition makes sure that we do not interpolate functions
// which are basically components used as a selectors.
styleArg.__emotion_real !== styleArg
) {
// If the type is function, we need to define the default theme.
transformedStyleArg = (props) => {
const resolvedStyles = styleArg({
...props,
theme: resolveTheme({ ...props, defaultTheme, themeId }),
});

let optionalVariants;
if (isPlainObject(resolvedStyles)) {
if (resolvedStyles && resolvedStyles.variants) {
optionalVariants = resolvedStyles.variants;
}
delete resolvedStyles['variants'];
}
let result = resolvedStyles;

const variantsStyles = variantsResolver(
props,
transformVariants(optionalVariants),
optionalVariants,
);

// the variantsStyle is an array of all variant styles that need to be applied,
// so we need to merge them on top of the rest of the styles
if (variantsStyles) {
variantsStyles.forEach((variantStyle) => {
result = deepmerge(result, variantStyle);
});
}

return result;
};
}

if (componentName && overridesResolver) {
Expand Down Expand Up @@ -242,43 +347,7 @@ export default function createStyled(input = {}) {
// If the type is array, than we need to add placeholders in the template for the overrides, variants and the sx styles.
transformedStyleArg = [...styleArg, ...placeholders];
transformedStyleArg.raw = [...styleArg.raw, ...placeholders];
} else if (
typeof styleArg === 'function' &&
// On the server Emotion doesn't use React.forwardRef for creating components, so the created
// component stays as a function. This condition makes sure that we do not interpolate functions
// which are basically components used as a selectors.
styleArg.__emotion_real !== styleArg
) {
// If the type is function, we need to define the default theme.
transformedStyleArg = (props) => {
const resolvedStyles = styleArg({
...props,
theme: resolveTheme({ ...props, defaultTheme, themeId }),
});

let optionalVariants;
if (isPlainObject(resolvedStyles)) {
if (resolvedStyles && resolvedStyles.variants) {
optionalVariants = resolvedStyles.variants;
}
delete resolvedStyles['variants'];
}
let result = resolvedStyles;

const variantsStyles = variantsResolver(props, transformVariants(optionalVariants), optionalVariants);

// the variantsStyle is an array of all variant styles that need to be applied,
// so we need to merge them on top of the rest of the styles
if(variantsStyles) {
variantsStyles.forEach(variantStyle => {
result = deepmerge(result, variantStyle);
});
}

return result;
}
}

const Component = defaultStyledResolver(transformedStyleArg, ...expressionsWithDefaultTheme);

if (process.env.NODE_ENV !== 'production') {
Expand Down
153 changes: 102 additions & 51 deletions packages/mui-system/src/createStyled.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,17 +417,20 @@ describe('createStyled', () => {
const styled = createStyled({});

const Test = styled('div')({
variants: [{
props: { color: 'blue', variant: 'filled' },
style: {
backgroundColor: 'rgb(0,0,255)'
}
}, {
props: { color: 'blue', variant: 'text' },
style: {
color: 'rgb(0,0,255)'
}
}]
variants: [
{
props: { color: 'blue', variant: 'filled' },
style: {
backgroundColor: 'rgb(0,0,255)',
},
},
{
props: { color: 'blue', variant: 'text' },
style: {
color: 'rgb(0,0,255)',
},
},
],
});

// console.log("fn as arg");
Expand Down Expand Up @@ -462,55 +465,103 @@ describe('createStyled', () => {
// }),
// }));

const { getByTestId } = render(<><Test data-testid="filled" color="blue" variant="filled">Filled</Test><Test data-testid="text" color="blue" variant="text">Filled</Test></>);
expect(getByTestId('filled')).toHaveComputedStyle({backgroundColor: "rgb(0, 0, 255)" });
expect(getByTestId('text')).toHaveComputedStyle({color: "rgb(0, 0, 255)" });
const { getByTestId } = render(
<>
<Test data-testid="filled" color="blue" variant="filled">
Filled
</Test>
<Test data-testid="text" color="blue" variant="text">
Filled
</Test>
</>,
);
expect(getByTestId('filled')).toHaveComputedStyle({ backgroundColor: 'rgb(0, 0, 255)' });
expect(getByTestId('text')).toHaveComputedStyle({ color: 'rgb(0, 0, 255)' });
});

it('should accept variants in function style arg', () => {
const styled = createStyled({ defaultTheme: { colors: { blue: 'rgb(0, 0, 255)'}}});
const styled = createStyled({ defaultTheme: { colors: { blue: 'rgb(0, 0, 255)' } } });

const Test = styled('div')(({ theme }) => ({
variants: [{
props: { color: 'blue', variant: 'filled' },
style: {
backgroundColor: theme.colors.blue
}
}, {
props: { color: 'blue', variant: 'text' },
style: {
color: theme.colors.blue
}
}]
variants: [
{
props: { color: 'blue', variant: 'filled' },
style: {
backgroundColor: theme.colors.blue,
},
},
{
props: { color: 'blue', variant: 'text' },
style: {
color: theme.colors.blue,
},
},
],
}));

const { getByTestId } = render(<><Test data-testid="filled" color="blue" variant="filled">Filled</Test><Test data-testid="text" color="blue" variant="text">Filled</Test></>);
expect(getByTestId('filled')).toHaveComputedStyle({backgroundColor: "rgb(0, 0, 255)" });
expect(getByTestId('text')).toHaveComputedStyle({color: "rgb(0, 0, 255)" });
const { getByTestId } = render(
<>
<Test data-testid="filled" color="blue" variant="filled">
Filled
</Test>
<Test data-testid="text" color="blue" variant="text">
Filled
</Test>
</>,
);
expect(getByTestId('filled')).toHaveComputedStyle({ backgroundColor: 'rgb(0, 0, 255)' });
expect(getByTestId('text')).toHaveComputedStyle({ color: 'rgb(0, 0, 255)' });
});

// TODO: Add support in array definitions
// it.only('should accept variants in arrays', () => {
// const styled = createStyled({});

// const Test = styled('div')([() => ({
// variants: [{
// props: { color: 'blue', variant: 'filled' },
// style: {
// backgroundColor: 'rgb(0,0,255)'
// }
// }, {
// props: { color: 'blue', variant: 'text' },
// style: {
// color: 'rgb(0,0,255)'
// }
// }]
// })]);

// const { getByTestId } = render(<><Test data-testid="filled" color="blue" variant="filled">Filled</Test><Test data-testid="text" color="blue" variant="text">Filled</Test></>);
// expect(getByTestId('filled')).toHaveComputedStyle({backgroundColor: "rgb(0, 0, 255)" });
// expect(getByTestId('text')).toHaveComputedStyle({color: "rgb(0, 0, 255)" });
// });
it('should accept variants in arrays', () => {
const styled = createStyled({});

const Test = styled('div')(
() => ({
variants: [
{
props: { color: 'blue', variant: 'filled' },
style: {
backgroundColor: 'rgb(0,0,255)',
},
},
{
props: { color: 'blue', variant: 'text' },
style: {
color: 'rgb(0,0,255)',
},
},
],
}),
{
variants: [
{
props: { color: 'blue', variant: 'outlined' },
style: {
borderTopColor: 'rgb(0,0,255)',
},
},
],
},
);

const { getByTestId } = render(
<>
<Test data-testid="filled" color="blue" variant="filled">
Filled
</Test>
<Test data-testid="text" color="blue" variant="text">
Filled
</Test>
<Test data-testid="outlined" color="blue" variant="outlined">
Outlined
</Test>
</>,
);
expect(getByTestId('filled')).toHaveComputedStyle({ backgroundColor: 'rgb(0, 0, 255)' });
expect(getByTestId('text')).toHaveComputedStyle({ color: 'rgb(0, 0, 255)' });
expect(getByTestId('outlined')).toHaveComputedStyle({ borderTopColor: 'rgb(0, 0, 255)' });
});

// TODO: Add tests for priority of style definition
});
Expand Down

0 comments on commit 0414435

Please sign in to comment.