Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[system] Don't spread props to DOM for string tags #33761

Merged
merged 6 commits into from Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/mui-system/src/createStyled.js
Expand Up @@ -8,6 +8,17 @@ function isEmpty(obj) {
return Object.keys(obj).length === 0;
}

// https://github.com/emotion-js/emotion/blob/26ded6109fcd8ca9875cc2ce4564fee678a3f3c5/packages/styled/src/utils.js#L40
function isStringTag(tag) {
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;
Expand Down Expand Up @@ -104,6 +115,9 @@ export default function createStyled(input = {}) {
} else if (componentSlot) {
// any other slot specified
shouldForwardPropOption = slotShouldForwardProp;
} else if (isStringTag(tag)) {
// for string (html) tag, preserve the behavior in emotion & styled-components.
shouldForwardPropOption = undefined;
}

const defaultStyledResolver = styledEngineStyled(tag, {
Expand Down
53 changes: 53 additions & 0 deletions packages/mui-system/src/createStyled.test.js
Expand Up @@ -281,4 +281,57 @@ describe('createStyled', () => {
});
});
});

it('does not spread `sx` prop to DOM', () => {
const styled = createStyled({});
const Button = styled('button')({});

const { container } = render(<Button sx={{ bgcolor: 'red' }}>Link</Button>);
expect(container.firstChild).not.to.have.attribute('sx');
});

it('does not forward `ownerState` prop to DOM', () => {
const styled = createStyled({});
const Button = styled('button')({});

const { container } = render(<Button ownerState={{}} />);
expect(container.firstChild).not.to.have.attribute('ownerState');
});

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(
<Button color="red" shouldBeRemoved data-foo="bar">
Link
</Button>,
);
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(<Button as="a" href="/" />);

expect(container.firstChild).to.have.tagName('a');
expect(container.firstChild).to.have.attribute('href', '/');
});

it('able to pass props to `as` styled component', () => {
const styled = createStyled({});
const ChildRoot = styled('div')({});
const Child = ({ component }) => <ChildRoot as={component}>content</ChildRoot>;
const Button = styled('button')({});
const { container } = render(<Button as={Child} component="span" />);

expect(container.firstChild).to.have.tagName('span');
});
});
});