-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Include .defaultProps
in the documentation
#2573
Comments
Could you post some code to show how you expected it work? And how you finally got it to work? |
The difficult part for me was just finding out that this method existed.
|
The currently recommended way to do default props is with object destructuring default values. I would implement your use case like this — though the way you have done it is perfectly valid too. function UnstyledTitle({ variant = 'h3', className, children }) {
return <Typography variant={variant} className={className}>{children}</Typography>
}
const Title = styled(UnstyledTitle)`
display: flex;
flex-wrap: wrap;
align-self: start;
@media (min-width:528px) {
align-self: center;
margin-left: .25em;
}
` |
Could I do the above but with an anonymous function? The reason I think this could be beneficial to include in the docs is because people coming from styled-components are used to having the |
You can always use an arrow function in place of a normal named function. I would choose the option that makes your code more readable. @Andarist Is there any reason for us to add an API like styled-components' |
Now that For example, consider what it would look like to extend some component Before, with defaultProps: const ExtendedFlex = styled(Flex)`
// ... css rules ...
`;
ExtendedFlex.defaultProps = { gap: "0", align: "stretch", justify: "stretch" }; After, using "recommended" approach: function UnstyledExtendedFlex({
gap = "0",
align = "stretch",
justify = "stretch",
...props
}: Parameters<typeof Flex>[0]) {
return <Flex gap={gap} align={align} justify={justify} {...props} />;
}
const ExtendedFlex = styled(UnstyledExtendedFlex)`
// ... css rules ...
`; This is quite a lot of boilerplate for just a few default props. An |
In my repo
// Convert defaultProps to default function parameters
// npx jscodeshift --parser=tsx --extensions=tsx,ts -t migrate-defaultProps.ts components/
import { API, FileInfo, Options } from 'jscodeshift';
const transform = (file: FileInfo, api: API, options: Options) => {
const j = api.jscodeshift;
const root = j(file.source);
// Find the imported component name dynamically
let styledComponentName;
root
.find(j.CallExpression, {
callee: {
name: 'styled',
},
})
.forEach((path) => {
if (
path.value.arguments.length > 0 &&
path.value.arguments[0].type === 'Identifier'
) {
styledComponentName = path.value.arguments[0].name;
}
});
// Find the styled component's template literal
let styledTemplateLiteral;
root.find(j.TaggedTemplateExpression).forEach((path) => {
if (
path.value.tag.type === 'CallExpression' &&
path.value.tag.callee.name === 'styled' &&
path.value.tag.arguments[0].name === styledComponentName
) {
styledTemplateLiteral = path.value.quasi;
}
});
// Find the property name dynamically
let propertyName;
root.find(j.AssignmentExpression).forEach((path) => {
if (
path.value.left.property &&
path.value.left.property.name === 'defaultProps'
) {
propertyName = Object.keys(
path.value.right.properties.reduce((acc, prop) => {
acc[prop.key.name] = true;
return acc;
}, {}),
)[0];
}
});
root
.find(j.AssignmentExpression, {
left: {
type: 'MemberExpression',
property: {
name: 'defaultProps',
},
},
})
.forEach((path) => {
const componentName = path.value.left.object.name;
const defaultProps = path.value.right;
// Create a new functional component with default parameters
const newComponent = j.functionDeclaration(
j.identifier(`Unstyled${componentName}`),
[
j.objectPattern([
// Add default parameters to the new component
...defaultProps.properties.map((prop) => {
const id = j.identifier(prop.key.name);
let defaultValue;
// Check if the value is an array expression
if (prop.value.type === 'ArrayExpression') {
defaultValue = j.arrayExpression(prop.value.elements);
} else {
// For literals, use the literal value
defaultValue = j.literal(prop.value.value);
}
// Create an assignment pattern for default values
const assignmentPattern = j.assignmentPattern(id, defaultValue);
const property = j.property('init', id, assignmentPattern);
property.shorthand = true; // Enable shorthand syntax
return property;
}),
// Spread the rest of the properties
j.restElement(j.identifier('props')),
]),
],
j.blockStatement([
j.returnStatement(
j.jsxElement(
j.jsxOpeningElement(
j.jsxIdentifier(styledComponentName),
[
// Spread props into the Box component
j.jsxSpreadAttribute(j.identifier('props')),
// spread the rest of the properties
...defaultProps.properties.map((prop) => {
return j.jsxAttribute(
j.jsxIdentifier(prop.key.name),
j.jsxExpressionContainer(j.identifier(prop.key.name)),
);
}),
],
true,
),
null,
[],
),
),
]),
);
// Replace the old component with the new one
root
.find(j.ImportDeclaration)
.at(-1)
.forEach((importPath) => {
j(importPath).insertAfter(newComponent);
});
root
.find(j.VariableDeclaration)
.filter(
(variablePath) =>
variablePath.value.declarations[0].id.name === componentName,
)
.forEach((variablePath) => {
variablePath.value.declarations[0].init = j.taggedTemplateExpression(
j.callExpression(j.identifier('styled'), [
j.identifier('Unstyled' + componentName),
]),
styledTemplateLiteral,
);
});
j(path).remove();
});
return root.toSource({ quote: 'single' });
};
export default transform; Input: import styled from '@emotion/styled';
import { Box } from 'rebass';
const Container = styled(Box)`
margin-left: auto;
margin-right: auto;
max-width: 1310px;
`;
Container.defaultProps = {
px: 3,
};
export default Container; Output: import styled from '@emotion/styled';
import { Box } from 'rebass';
function UnstyledContainer(
{
px = 3,
...props
}
) {
return <Box {...props} px={px} />;
}
const Container = styled(UnstyledContainer)`
margin-left: auto;
margin-right: auto;
max-width: 1310px;
`;
export default Container; I run the script like this: npx jscodeshift --parser=tsx --extensions=tsx,ts -t migrate-defaultProps.ts components/Container/index.tsx Or, to run on a whole directory: npx jscodeshift --parser=tsx --extensions=tsx,ts -t migrate-defaultProps.ts components/ |
I've been looking into potentially migrating a large codebase from I think it makes a lot of sense like this: const SendIcon = styled(Icon).attrs({ iconId: 'send--filled' })`
background: orange;
`; |
Isn't this an option? const StyledLink = styled(
({underline = "none", ...props}: LinkTypeMap["props"]) => <MuiLink underline={underline} {...props}/>
)`
padding: ${({theme}) => `${theme.spacing(1)} 0`};
display: inline-block;
&.current {
color: ${({theme}) => theme.palette.common.black};
}
` as typeof MuiLink; Not too far off of the attrs approach. And compatible to all IDE's SCSS code block highlighting. |
Agree with @lsegal . There is a clear need for a concise alternative to |
Description:
It took me forever to figure out how to set default props with emotion's styled components. This isn't explained anywhere in the emotion docs.
Documentation links:
https://emotion.sh/docs/styled
The text was updated successfully, but these errors were encountered: