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

feat: Migrate button components to TypeScript #1017

Merged
merged 14 commits into from
Feb 8, 2021
Merged

Conversation

smockle
Copy link
Member

@smockle smockle commented Feb 4, 2021

This is classified as a “major” change, because I removed default exports from the individual button component files, following TypeScript best practices. I retained the default export in the barrel (index.ts) for backwards compatibility (we could do the same in other files, exporting both named and default exports).

Updated per the strategy @colebemis outlined below.

Part of #970

Screenshots

Please provide before/after screenshots for any visual changes

Merge checklist

  • Added or updated TypeScript definitions (index.d.ts) if necessary
  • Added/updated tests
  • Added/updated documentation
  • Tested in Chrome
  • Tested in Firefox
  • Tested in Safari
  • Tested in Edge

Take a look at the What we look for in reviews section of the contributing guidelines for more information on how we review PRs.

@changeset-bot
Copy link

changeset-bot bot commented Feb 4, 2021

🦋 Changeset detected

Latest commit: 7a24774

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@primer/components Patch

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

@vercel
Copy link

vercel bot commented Feb 4, 2021

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/primer/primer-components/c21svfo4m
✅ Preview: https://primer-components-git-smockle-button.primer.now.sh

@smockle smockle marked this pull request as draft February 4, 2021 21:21
@vercel vercel bot temporarily deployed to Preview February 5, 2021 04:47 Inactive
@smockle smockle marked this pull request as ready for review February 5, 2021 04:47
Copy link
Contributor

@colebemis colebemis left a 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.

@colebemis colebemis mentioned this pull request Feb 5, 2021
53 tasks
@smockle
Copy link
Member Author

smockle commented Feb 5, 2021

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. 👍

@vercel vercel bot temporarily deployed to Preview February 5, 2021 18:13 Inactive
@vercel vercel bot temporarily deployed to Preview February 5, 2021 20:06 Inactive
@smockle
Copy link
Member Author

smockle commented Feb 5, 2021

In a4ffc03, I did three things:

  1. Restored propTypes
  2. Restored default exports
  3. Renamed type used for styled components props

This is ready for another review! 🚀

@vercel vercel bot temporarily deployed to Preview February 5, 2021 20:09 Inactive
microsoft/TypeScript#27956
> These errors occur when you have subdirectories of a typeRoots directory…that do not contain index.d.ts files.
@vercel vercel bot temporarily deployed to Preview February 5, 2021 20:19 Inactive
@vercel vercel bot temporarily deployed to Preview February 5, 2021 20:34 Inactive
Copy link
Contributor

@colebemis colebemis left a 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


const Button = styled(ButtonBase)`
const Button = styled(ButtonBase)<ButtonBaseProps>`
Copy link
Contributor

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.

Suggested change
const Button = styled(ButtonBase)<ButtonBaseProps>`
const Button = styled(ButtonBase)`

Copy link
Member Author

@smockle smockle Feb 6, 2021

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?

Copy link
Member Author

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

Copy link
Member Author

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: The explicit type restored in 7f8997e was amended in 80317da, because sx was apparently not composeable, so I applied it separately.

Comment on lines 31 to 37
type StyledButtonBaseProps = {
as?: 'button' | 'a' | 'summary' | 'input' | string | React.ReactType
fontSize?: FontSizeProps['fontSize']
variant?: 'small' | 'medium' | 'large'
} & SystemCommonProps &
SystemLayoutProps &
SxProp
Copy link
Contributor

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?

Suggested change
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

Copy link
Contributor

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?

Copy link
Member Author

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! 👍

Copy link
Member Author

@smockle smockle Feb 6, 2021

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)

Copy link
Member Author

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.

Copy link
Member Author

@smockle smockle Feb 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7f8997e and 80317da

src/Button/ButtonGroup.tsx Outdated Show resolved Hide resolved
src/Button/ButtonOutline.tsx Outdated Show resolved Hide resolved
@@ -0,0 +1,9 @@
export {Button as default} from './Button'
export {Button} from './Button'
Copy link
Contributor

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?

Copy link
Member Author

@smockle smockle Feb 6, 2021

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.)

Copy link
Member Author

@smockle smockle Feb 6, 2021

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?

Copy link
Member Author

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.

Copy link
Member Author

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.

import systemPropTypes from '@styled-system/prop-types'
import {FontSizeProps} from 'styled-system'

export const systemStyles = compose(fontSize, COMMON, LAYOUT)
Copy link
Contributor

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:

Suggested change
export const systemStyles = compose(fontSize, COMMON, LAYOUT)
export const buttonSystemProps = compose(fontSize, COMMON, LAYOUT)

Copy link
Contributor

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.

Copy link
Member Author

@smockle smockle Feb 6, 2021

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 adding FontSizeProps & SystemCommonProps & SystemLayoutProps to the StyledButtonBaseProps type.

Great idea—this is an elegant way to address #1017 (comment). 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by 7f8997e and 80317da

@vercel vercel bot temporarily deployed to Preview February 6, 2021 17:56 Inactive
Instead, share these via a different type ('ButtonSystemProps').
@vercel vercel bot temporarily deployed to Preview February 6, 2021 18:24 Inactive
@vercel vercel bot temporarily deployed to Preview February 6, 2021 18:29 Inactive
@vercel vercel bot temporarily deployed to Preview February 6, 2021 19:05 Inactive
@vercel vercel bot temporarily deployed to Preview February 6, 2021 19:12 Inactive
@smockle
Copy link
Member Author

smockle commented Feb 6, 2021

From @colebemis in #1017 (review):

Looks great! Just left a few minor comments

🎊 Thanks for reviewing! I’ve addressed each of the comments, so this is ready for another review.

@colebemis
Copy link
Contributor

I think this is pretty much good to go! 🎉 @smockle Can you update the test files and snapshots as well?

@vercel vercel bot temporarily deployed to Preview February 8, 2021 19:34 Inactive
@smockle
Copy link
Member Author

smockle commented Feb 8, 2021

I think this is pretty much good to go! 🎉 @smockle Can you update the test files and snapshots as well?

Thanks! Done, in 6e411c4 👍

@vercel vercel bot temporarily deployed to Preview February 8, 2021 19:40 Inactive
Copy link
Contributor

@colebemis colebemis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wooo! Nice work 🎉

@colebemis colebemis merged commit 7a73fe5 into main Feb 8, 2021
@colebemis colebemis deleted the smockle/button branch February 8, 2021 20:06
@github-actions github-actions bot mentioned this pull request Feb 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants