From f7af0d6e8c1f700f1c1af0e59bb09f4bff8302f2 Mon Sep 17 00:00:00 2001 From: siriwatknp Date: Sun, 24 Jul 2022 21:54:32 +0700 Subject: [PATCH 1/5] add valid html check --- packages/mui-system/package.json | 1 + packages/mui-system/src/createStyled.js | 18 +++++++ packages/mui-system/src/createStyled.test.js | 55 ++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/packages/mui-system/package.json b/packages/mui-system/package.json index 2299b3dc182eb8..88a717ba17c451 100644 --- a/packages/mui-system/package.json +++ b/packages/mui-system/package.json @@ -58,6 +58,7 @@ }, "dependencies": { "@babel/runtime": "^7.17.2", + "@emotion/is-prop-valid": "^1.1.3", "@mui/private-theming": "^5.9.1", "@mui/styled-engine": "^5.8.7", "@mui/types": "^7.1.4", diff --git a/packages/mui-system/src/createStyled.js b/packages/mui-system/src/createStyled.js index ee5c9fb06a6ebc..eee03b384dc52f 100644 --- a/packages/mui-system/src/createStyled.js +++ b/packages/mui-system/src/createStyled.js @@ -1,3 +1,7 @@ +// Both emotion & styled-components is using `@emotion/is-prop-valid` +// emotion: https://github.com/emotion-js/emotion/blob/26ded6109f/packages/styled/src/utils.js +// styled-components: https://github.com/styled-components/styled-components/blob/38256f562b/packages/styled-components/src/models/StyledComponent.ts +import isPropValid from '@emotion/is-prop-valid'; import styledEngineStyled from '@mui/styled-engine'; import { getDisplayName } from '@mui/utils'; import createTheme from './createTheme'; @@ -8,6 +12,17 @@ function isEmpty(obj) { return Object.keys(obj).length === 0; } +function isStringTag(tag) { + // https://github.com/emotion-js/emotion/blob/26ded6109fcd8ca9875cc2ce4564fee678a3f3c5/packages/styled/src/utils.js#L40 + return ( + typeof tag === 'string' && + // 96 is one less than the char code + // for "a" so this is checking that + // it's a lowercase character + tag.charCodeAt(0) > 96 + ); +} + const getStyleOverrides = (name, theme) => { if (theme.components && theme.components[name] && theme.components[name].styleOverrides) { return theme.components[name].styleOverrides; @@ -104,6 +119,9 @@ export default function createStyled(input = {}) { } else if (componentSlot) { // any other slot specified shouldForwardPropOption = slotShouldForwardProp; + } else if (isStringTag(tag)) { + // if no `slot` specified and tag is string (html), preserve the behavior in emotion & styled-components. + shouldForwardPropOption = (prop) => isPropValid(prop) && shouldForwardProp(prop); } const defaultStyledResolver = styledEngineStyled(tag, { diff --git a/packages/mui-system/src/createStyled.test.js b/packages/mui-system/src/createStyled.test.js index 39be9742a592f6..df8932b3e7d957 100644 --- a/packages/mui-system/src/createStyled.test.js +++ b/packages/mui-system/src/createStyled.test.js @@ -281,4 +281,59 @@ describe('createStyled', () => { }); }); }); + + it('does not spread `sx` prop to DOM', () => { + const styled = createStyled({}); + const Button = styled('button')({}); + + const { container } = render(); + expect(container.firstChild).not.to.have.attribute('sx'); + }); + + describe('default behaviors', () => { + it('does not forward invalid props to DOM if no `slot` specified', () => { + // This scenario is usually used by library consumers + const styled = createStyled({}); + const Button = styled('button')({}); + + const { container } = render( + , + ); + expect(container.firstChild.getAttribute('data-foo')).to.equal('bar'); + expect(container.firstChild.getAttribute('color')).to.equal('red'); // color is for Safari mask-icon link + expect(container.firstChild.getAttribute('shouldBeRemoved')).not.to.equal('true'); + }); + + it('can use `as` prop', () => { + const styled = createStyled({}); + const Button = styled('button')({}); + + const { container } = render(