-
Notifications
You must be signed in to change notification settings - Fork 538
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
feat: Migrate button components to TypeScript #1017
Conversation
🦋 Changeset detectedLatest commit: 7a24774 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This pull request is being automatically deployed with Vercel (learn more). 🔍 Inspect: https://vercel.com/primer/primer-components/c21svfo4m |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great, @smockle! Thanks for taking this on ❤️
A few thoughts:
propTypes
It looks like tests are failing because of missing propTypes. Let's not remove propTypes in this PR. Instead, I'd rather remove propTypes from all the components (and update tests) at once in one PR during the cleanup phase of this epic.
default exports
I'm on board with moving away from default exports. However, like propTypes, I think we should remove all the default exports (and add a linting rule) at once in one PR during the cleanup phase (I'll add that to our epic planning issue). That way we can batch breaking changes into one release instead of spreading them across multiple releases.
naming
I noticed you've named the types passed to styled-components ___InternalProps
. In other components, we use the name Styled___Props
for similar types. I don't have a strong opinion about what naming convention we should use (I actually prefer your naming) but I think we should be consistent. So let's either rename the types in this PR to Styled___Props
or rename the types in other components to ___InternalProps
.
Great feedback, thanks! I appreciate you taking the time to review and to clearly state the next steps. I agree 100% re: scoping the PR down (i.e. keeping propTypes and default exports) and re: making its names consistent with what we use elsewhere. 👍 |
… styled components.
In a4ffc03, I did three things:
This is ready for another review! 🚀 |
microsoft/TypeScript#27956 > These errors occur when you have subdirectories of a typeRoots directory…that do not contain index.d.ts files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Just left a few minor comments
src/Button/Button.tsx
Outdated
|
||
const Button = styled(ButtonBase)` | ||
const Button = styled(ButtonBase)<ButtonBaseProps>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think styled can infer the props accepted by ButtonBase
. It's worth testing out though.
const Button = styled(ButtonBase)<ButtonBaseProps>` | |
const Button = styled(ButtonBase)` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, it looks like you’re right! Out of curiosity—when is an explicit type necessary? I noticed one is provided in Avatar, Grid, and StyledOcticon, so I thought it was a Primer code style convention and tried to follow it here. I’m now realizing there might be a specific need for it in those cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed <ButtonBaseProps>
from Button
and similar in 1d60b55
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: An explicit type was restored in 7f8997e, which added ButtonSystemProps
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/Button/ButtonBase.tsx
Outdated
type StyledButtonBaseProps = { | ||
as?: 'button' | 'a' | 'summary' | 'input' | string | React.ReactType | ||
fontSize?: FontSizeProps['fontSize'] | ||
variant?: 'small' | 'medium' | 'large' | ||
} & SystemCommonProps & | ||
SystemLayoutProps & | ||
SxProp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, is there a reason you didn't write it this way?
type StyledButtonBaseProps = { | |
as?: 'button' | 'a' | 'summary' | 'input' | string | React.ReactType | |
fontSize?: FontSizeProps['fontSize'] | |
variant?: 'small' | 'medium' | 'large' | |
} & SystemCommonProps & | |
SystemLayoutProps & | |
SxProp | |
type StyledButtonBaseProps = { | |
as?: 'button' | 'a' | 'summary' | 'input' | string | React.ReactType | |
variant?: 'small' | 'medium' | 'large' | |
} & FontSizeProps & | |
SystemCommonProps & | |
SystemLayoutProps & | |
SxProp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, also isn't the type incorrect because ButtonBase
doesn't actually accept common, layout, and fontsize props?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, is there a reason you didn't write it this way?
It’s possible I thought (incorrectly) that FontSizeProps
includes keys not used by ButtonBase
(i.e. in addition to 'fontSize'
)—but it’s more likely the intersection simply didn’t occur to me 😅
…[I]sn't the type incorrect because ButtonBase doesn't actually accept common, layout, and fontsize props?
I’d defined it this way to avoid needing to add those to custom types for Button
, ButtonDanger
, ButtonInvisible
, ButtonOutline
and ButtonPrimary
(which all use fontSize
, COMMON
, LAYOUT
, and sx
), in order to make ButtonBaseProps
more useful as a base. It didn’t occur to me that—in doing so—I’d made ButtonBase
’s prop type itself wrong, especially because ButtonBase
is never (that I know of) rendered outside one of those components.
It definitely makes sense to clean that up (i.e. to type ButtonBase
’s props correctly). Thanks for catching this! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It definitely makes sense to clean that up…
Note: @colebemis gave a good suggestion for how to do this DRYly in #1017 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sidebar: Is it true that “ButtonBase
doesn't actually accept [a] fontsize prop”? The propTypes
in the current published version includes fontSize
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/Button/index.ts
Outdated
@@ -0,0 +1,9 @@ | |||
export {Button as default} from './Button' | |||
export {Button} from './Button' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the props types be exported here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There’s no right answer; it depends on the ergonomics we want to provide. It’s common for barrels to re-export all exports (which would include type exports), e.g. from the TypeScript Deep Dive: Barrel page:
// demo/index.ts
export * from './foo'; // re-export all of its exports
export * from './bar'; // re-export all of its exports
export * from './baz'; // re-export all of its exports
(Though I think this specific syntax is incompatible with default exports, so this may be something we choose to revisit after we replace them with named exports.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this specific syntax is incompatible with default exports
The answer to this isn’t clearer to me after reading the MDN export
page and the ECMA-262 spec.
If you export *
from two files which each have a default
export, which one is bound to the barrel’s default
export?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested this in a sandbox. I get two errors that Module '"/sandbox/src/module-aggregate"' has no default export.
and 'export *' does not re-export a default.
, but it still works. 😱 That is, the first re-exported function appears to be bound to the aggregate (barrel)’s default
, contra the error messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting export *
aside, we can go ahead and export the named prop types explicitly. Added this via e3aed73.
src/Button/ButtonBase.tsx
Outdated
import systemPropTypes from '@styled-system/prop-types' | ||
import {FontSizeProps} from 'styled-system' | ||
|
||
export const systemStyles = compose(fontSize, COMMON, LAYOUT) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe let's give this a better name:
export const systemStyles = compose(fontSize, COMMON, LAYOUT) | |
export const buttonSystemProps = compose(fontSize, COMMON, LAYOUT) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this gets exported from this file instead of being applied to ButtonBase. Maybe we should export a ButtonSystemProps
type that the other components can use instead of adding FontSizeProps & SystemCommonProps & SystemLayoutProps
to the StyledButtonBaseProps
type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe let's give this a better name:
Since systemStyles
is the name used in the current published version, would renaming it (which I agree we should do, for the reasons you stated) be a breaking change?
Maybe we should export a
ButtonSystemProps
type that the other components can use instead of addingFontSizeProps & SystemCommonProps & SystemLayoutProps
to theStyledButtonBaseProps
type.
Great idea—this is an elegant way to address #1017 (comment). 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead, share these via a different type ('ButtonSystemProps').
…m other 'buttonSystemProps'
From @colebemis in #1017 (review):
🎊 Thanks for reviewing! I’ve addressed each of the comments, so this is ready for another review. |
I think this is pretty much good to go! 🎉 @smockle Can you update the test files and snapshots as well? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wooo! Nice work 🎉
This is classified as a “major” change, because I removeddefault
exports from the individual button component files, following TypeScript best practices. I retained thedefault
export in the barrel (index.ts
) for backwards compatibility (we could do the same in other files, exporting both named anddefault
exports).Updated per the strategy @colebemis outlined below.
Part of #970
Screenshots
Please provide before/after screenshots for any visual changes
Merge checklist
index.d.ts
) if necessaryTake a look at the What we look for in reviews section of the contributing guidelines for more information on how we review PRs.