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
[core:feature] Add Clickable component to wrap ClickableBehavior (wip) #387
Conversation
Deploy preview for wonder-blocks ready! Built with commit 41a5811 |
Codecov Report
@@ Coverage Diff @@
## master #387 +/- ##
==========================================
+ Coverage 94.81% 94.85% +0.04%
==========================================
Files 111 112 +1
Lines 1619 1633 +14
Branches 321 325 +4
==========================================
+ Hits 1535 1549 +14
Misses 79 79
Partials 5 5
Continue to review full report at Codecov.
|
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.
Very cool! It seems like this is going to be quite useful. Pushing back as I think we'd really benefit from some more-detailed documentation, especially making it clear when we expect people to be using this (or not).
@@ -0,0 +1,141 @@ | |||
// @flow | |||
import * as React from "react"; | |||
import {StyleSheet} from "aphrodite"; |
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.
Before we add this to core - we should ask if we want this in core (since otherwise will require a version change later to move it). I guess if we ever wanted to move getClickableBehavior
out of core and this into a "clickable" package we'd have to bump the version anyway.
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.
Good call. Maybe we could create a wonder-blocks-clickable
package. We can move ClickableBehavior
over that package but still have wonder-blocks-core
export ClickableBehavior
though I think we'd want to eventually drop that export and have webapp just use Clickable
and have ClickableBehavior
be completely private.
const StyledButton = addStyle<"button">("button"); | ||
const StyledLink = addStyle<typeof Link>(Link); | ||
|
||
export default class Clickable extends React.Component<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.
Can you add a detailed comment here explaining how this component should be used? I think you can move the comment that you currently have at the top of clickable.md
into here and maybe expand it a bit explaining the usage and especially explaining when you should be using it and not Link or Button.
import type {StyleType} from "../util/types.js"; | ||
|
||
type Props = {| | ||
"aria-label": 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.
Can you add comments to these props so that they show up in the documentation? You can probably copy most of them from Link or Button.
> | ||
{content} | ||
</StyledButton> | ||
); |
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.
Tempted to tidy this up by having some separate methods like maybeRenderStyledButton
, etc. and then have them control the conditions of if they render. Or at least have renderStyledButton
and have it extract the props it needs. I think it would make the code a little easier to follow.
import * as React from "react"; | ||
import {StyleSheet} from "aphrodite"; | ||
|
||
import {Link} from "react-router-dom"; |
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 arriving a bit late to the party.... just wondering, is there a strong reason for including react-router-dom
as a WB dependency? instead of letting webapp
handling directly the routing? Asking these questions b/c I would like to understand the context 😃
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 wish we didn't, but we wanted to be able to use client-side routing with wonder-blocks components and being able to set href="/foo/bar"
on components seemed a convenient way to specify routes. I couldn't figure how to make this work without importing Link
from react-router-dom
within wonder-blocks. 😅
<View> | ||
<Clickable> | ||
{(state) => { | ||
console.log(state); |
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 this line could be removed?
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 can't formally "request changes" on a PR that I'm the author. I've left some comments that should be addressed. In particular deduplicating the types.js, removing the old files from wonder-blocks-core, and adding comments for the props in clickable.js stand out.
{...commonProps} | ||
to={href} | ||
onClick={onClick} | ||
aria-disabled={disabled ? "true" : undefined} |
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 looks better than what we had before. Nice!
} | ||
|
||
const styles = StyleSheet.create({ | ||
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.
Wowza! If you grabbed this reset from somewhere please a link a 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.
this was a part of the old Clickable component i took over
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... I wonder where I found it from. 🤔 😅
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 looks like it was based on https://gist.github.com/MoOx/9137295. Can you add a link to that file in a comment?
@@ -0,0 +1,39 @@ | |||
{ | |||
"name": "@khanacademy/wonder-blocks-clickable", | |||
"version": "1.0.0", |
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.
With new packages I'd usually say start this at 0.1.0
and set private: true
, but given this is a very small package, I think we can get it done in a single PR so this should be okay.
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'd err on the side of staying private: true
and version 0.1.0
. It's easier to go from this to public release than to discover a problem at public release and have to back pedal. I prefer to plan for the worst rather than hope for the best. What do you think?
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.
+1 for private: true
and adding 0.1.0
. By adding this initial version, lerna will try to publish 1.0.0
instead of 1.0.1
(or something similar). Nothing that impacts at all, but good for keeping semver consistency.
@@ -2,6 +2,7 @@ | |||
import type { | |||
ClickableHandlers, | |||
ClickableState, | |||
ClickableRole, |
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.
Now that wonder-blocks-clickable
is exporting this we can remove this from wonder-blocks-core
.
const activeHref = href && !disabled; | ||
const useClientRouter = router && !skipClientNav; | ||
|
||
if (activeHref && useClientRouter) { |
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.
What do you think about putting the main bulk of this inline function into a separate method or set of methods? I think it might make the code a little easier to read by reducing indentation?
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 agree, I think that’d be better
{content} | ||
</StyledLink> | ||
); | ||
} else if (activeHref && !useClientRouter) { |
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 can just be an if
since the previous condition returns.
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 feel like it’s better this way for clarity, what do you think?
{content} | ||
</StyledAnchor> | ||
); | ||
} else { |
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 else is unnecessary since both previous conditions return anyway.
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.
same as above
alignItems: "center", | ||
}, | ||
h1: { | ||
marginRight: "25px", |
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.
What do you think about using Wonder Blocks Spacing constants in our docs? Seems like this is an opportunity to encourage best practices.
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.
good idea!
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 can’t get spacing to work, when I apply it here it has no effect :/
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.
Have a look at the uses of wonder-blocks-spacing
in the other markdown 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 good. I have a few questions inline for your consideration.
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.
Some minor nits in clickable.md.
|
||
const styles = StyleSheet.create({ | ||
focused: { | ||
border: "none", |
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's hard to see this style since the unfocused state has border: "none"
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.
i’ll just remove it
<Clickable onClick={() => alert("You clicked some text!")}> | ||
{ | ||
eventState => | ||
<Body style={[ |
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.
Can you wrap this in a View to make it look more like a card and change the background color of it as the eventState changes?
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 for the changes. The only blocking issue now is that handlers
not being passed to getCorrectTag
.
<ClickableBehavior> | ||
{(state, handlers) => | ||
<ClickableBehavior onClick={this.props.onClick}> | ||
{(state) => |
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.
Not passing the handlers means that the state won't get update correctly.
@@ -18,12 +18,12 @@ type Props = {| | |||
children: (eventState: ClickableState) => React.Node, | |||
|
|||
/** | |||
* An onClick function which Clickable can execure | |||
* An onClick function which Clickable can execute when clicked |
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 for fixing my typos.
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.
LGTM. @jandrade will have to approve this PR though since I'm still its author.
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.
Everything looks good so far. Just added some comments that need to be addressed.
@@ -0,0 +1,39 @@ | |||
{ | |||
"name": "@khanacademy/wonder-blocks-clickable", | |||
"version": "1.0.0", |
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.
+1 for private: true
and adding 0.1.0
. By adding this initial version, lerna will try to publish 1.0.0
instead of 1.0.1
(or something similar). Nothing that impacts at all, but good for keeping semver consistency.
// Source: https://www.w3.org/WAI/PF/aria-1.1/states_and_properties | ||
// TODO(kevinb): convert to disjoint union of exact object types keyed on role | ||
// eslint-disable-next-line flowtype/require-exact-type | ||
export type AriaProps = {| |
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.
e.g. If you needed it, you shouldn't need to export AriaProps
here. Instead, you could import directly from wonderblocks-core
.
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 actually wasn’t being used at all, I removed it
// eslint-disable-next-line react/prop-types | ||
"aria-label": this.props["aria-label"], | ||
"data-test-id": this.props.testId, | ||
role: this.props.role, |
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 role
prop shouldn't be part of commonProps
b/c <button>
doesn't need to use this role.
More info here: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role#Best_practices
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 for catching that!
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.
LGTM! Great job implementing this component :). Thanks for addressing my comments.
}); | ||
|
||
test("disallow navigation when href and disabled are both set", () => { | ||
const wrapper = mount( |
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.
NIT: Missing // Arrange
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.
good catch :)
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.
LGTM
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.
Excellent, thank you!
I've wanted to create a
Clickable
component for some time now to makeClickableBehavior
easier to use. This needs comments, better docs, and tests, but I thought I put it up early so that people could see what I was thinking with this wrapper. I'd like to eventually update the existing component that useClickableBehavior
to useClickable
instead.