-
Notifications
You must be signed in to change notification settings - Fork 81
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
[WIP] feat(Button): add "as" prop to allow custom tags #263
Conversation
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.
Nice to have an example/stories to poke around with!! Thanks for contributing, this is going to be very useful.
Made some small comments but my concern (seeing the story examples and having more context now) is around losing type safety. Would love to have more discussion of this.
import classnames from 'classnames' | ||
import { deprecationWarning } from '../../deprecation' | ||
|
||
interface ButtonProps { | ||
type: 'button' | 'submit' | 'reset' | ||
disabled?: boolean | ||
as?: FunctionComponent<any> | ComponentClass<any> | string |
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.
Suggestion - use typescript unknown
instead of any
here.
@@ -22,14 +21,14 @@ interface ButtonProps { | |||
small?: boolean | |||
icon?: boolean | |||
unstyled?: boolean | |||
[x: string]: any |
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.
Hmmmm... what was this to address? 🤔 Was it to broaden the created element's props more? And is this broadening of props only necessary because we accept an as
prop as a string
name?
Feels like we are losing type safety, that gives me pause since we are creating a library to be used in a lot of unknown contexts, We are trying to wrap styles, yes, but also try to enable good practices around accessibility, responsiveness etc - so we may need constraints (to make sure that an anchor tag "button" has a role: button
for example).
If this was in an application where the use cases are known, its different.
If it solves the problem, I'd prefer us to limit as
types to FunctionComponent<any> | ComponentClass<any>
instead for the sake of this library. Someone can always pass in their anchor tag, already with any props/attributes that way - the functionality isn't lost.
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'm not sure if this line was the best way to do this.
I think we are losing type safety. It allows the user to add whatever prop they need. In the case of anchor tags, target
and rel
are fairly common to add. For accessibility purposes, aria-*
can be added. Users may also want to add data-*
. I added it to accommodate for the various use cases a user may have. I'm not sure what the best way is to move 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.
I think my exploration of how to extend props to allow valid property names in #267 is relevant to this.
import classnames from 'classnames' | ||
import { deprecationWarning } from '../../deprecation' | ||
|
||
interface ButtonProps { | ||
type: 'button' | 'submit' | 'reset' |
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.
Is this being removed because of redundancy?
We have tried to start a pattern of explicitly requiring props that seem essential to the component. Here are some new docs that outline this and a related comment.
Of course, these are opinions and we are still figuring it out. I'm curious your thoughts on it @christopherhuii . cc: @suzubara
@@ -12,6 +12,7 @@ describe('Button component', () => { | |||
|
|||
it('renders without errors', () => { | |||
const { queryByTestId } = render(<Button type="button">Click Me</Button>) | |||
console.log(queryByTestId('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.
console.leftovers
@@ -75,3 +75,30 @@ export const customClass = (): React.ReactElement => ( | |||
Click Me | |||
</Button> | |||
) | |||
|
|||
export const asAnchorTag = (): React.ReactElement => ( | |||
<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.
This seems like an example of where using as
props are any string
could be an issue- there's no way to verify, for example, that they are passing types that are valid for the anchor tag. Or that they even pass a valid html element tag name as a string...
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 we discussed this in Slack but I didn't actually formally comment here. I think the best way to handle this use case would be to create a <Link>
component (that takes a prop so it can be styled as a button) instead of modifying the Button component to render a different kind of element.
Changed status to WIP since what happens with #263 will influence the approach here. |
@christopherhuii @haworku @ahobson think we can close this in favor of the Link component work? |
Summary
Currently, the
Button
component only allows it to be a HTMLbutton
. This PR adds anas
prop to allow tags and props to be configurable.For example:
Related Issues or PRs
Closes #161
How To Test
View code, tests, and stories
Screenshots (optional)