If you know how to fix the issue, make a pull request instead.
@types/xxxx package and had problems.Definitions by: in index.d.ts) so they can respond.I initially asked on Stack Overflow, but posting here with the hopes I can get a little more traction.
Given:
const Link = styled.a`
border: solid 1px black;
border-radius: 5px;
padding: 5px;
margin: 10px 5px;
`;
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;
const LinkAsButton = styled(Link).attrs<ButtonProps>
>(({
as: 'button',
className: 'btn btn-primary'
})`
border: solid 1px red;
`;
How do I pass button specific props (e.g. disabled) to LinkAsButton?
Repo (includes branch for v3 and v4) demonstrating issue: https://github.com/arteforme/v3-v4-styled-components-migration
In v3, I'm able to do the following:
package.json
"dependencies": {
"@types/react": "^16.8.8",
"@types/react-dom": "^16.8.2",
"react": "^16.8.4",
"react-dom": "^16.8.4",
"react-scripts": "2.1.8",
"styled-components": "^3.3.3",
"typescript": "^3.3.3333"
}
Component definition
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;
const Link = styled.a`
border: solid 1px black;
border-radius: 5px;
padding: 5px;
margin: 10px 5px;
`;
const LinkAsButton = Link.withComponent('button')
.extend.attrs<ButtonProps>({
className: 'btn btn-primary'
})`
border: solid 1px red;
`;
and in consuming code, I can specify button specific props (e.g. disabled, form, etc) on the LinkAsButton component.
In v4, I'd like to achieve the same; however, I've not been successful in determining how to go about doing so. Per the latest documentation, I've updated LinkAsButton to no longer call extend or withComponent and instead wrap Link in a call to styled and uses as to specify the tag. The v4 version looks like:
package.json
dependencies": {
"@types/react": "^16.8.14",
"@types/styled-components": "^4.1.14",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "2.1.8",
"styled-components": "^4.2.0",
"typescript": "^3.4.5"
}
Component definition
const LinkAsButton = styled(Link).attrs<ButtonProps>
>(({
as: 'button',
className: 'btn btn-primary'
})`
border: solid 1px red;
`;
and in consuming code, I specify disabled={true} on a LinkAsButton instance, but this does not compile. Instead, the following error results:
_Property 'disabled' does not exist on type 'IntrinsicAttributes & Pick
Taking a look at the definition file, I notice that two generic arguments can be specified.
attrs <
U,
NewA extends Partial<StyledComponentPropsWithRef<C> & U> & {
[others: string]: any;
} = {}
> (
attrs: Attrs<StyledComponentPropsWithRef<C> & U, NewA, T>
): ThemedStyledFunction<C, T, O & NewA, A | keyof NewA>;
From what I can gather, C is the wrapped component and I believe NewA is NewAttributes. I'm not able to determine what U is in this case, but I try specifying the second generic parameter.
const LinkAsButton = styled(AnchorLink).attrs<{}, ButtonProps>`
// omitted for brevity
`
And in doing so, that results in the following error:
_Type 'ButtonHTMLAttributes
Types of property 'onCopy' are incompatible._
With that information in hand, I try:
const LinkAsButton = styled(Link).attrs<
{},
{ as: string; className: string; disabled?: boolean }
>({
as: 'button',
className: 'btn btn-primary'
})``;
Which works as I can now specify disabled in the markup, but not any other button specific props (e.g. form)
One approach would be to do the following:
const LinkAsButton = styled(Link)<ButtonProps>`
border: solid 1px red;
`;
Consuming code:
<div className="App">
<LinkAsButton as="button" className="btn-btn-primary" disabled={true}>
First button
</LinkAsButton>
<LinkAsButton as="button" className="btn-btn-primary" disabled={true}>
Second button
</LinkAsButton>
</div>
However, this would lead to a lot of code duplication :(.
Got it working by moving the generic param after the call to .attrs.
const LinkAsButton = styled(Link).attrs
>(({
as: 'button',
className: 'btn btn-primary'
})<ButtonProps>`
border: solid 1px red;
`;
Now any prop defined in ButtonProps can be set in the JSX
FYI Emotion decided not to implement attrs as they considered this feature as not required.
You could just do what you are doing now using defaultProps:
const LinkAsButton = styled(Link)<ButtonProps>`
border: solid 1px red;
`;
LinkAsButton.defaultProps = {
as: 'button',
className: 'btn btn-primary'
};
It's actually simpler for new comers and more readable.
Most helpful comment
FYI Emotion decided not to implement
attrsas they considered this feature as not required.You could just do what you are doing now using
defaultProps:It's actually simpler for new comers and more readable.