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

[core:feature] Add Clickable component to wrap ClickableBehavior (wip) #387

Merged
merged 20 commits into from Jul 9, 2019

Conversation

kevinbarabash
Copy link
Member

I've wanted to create a Clickable component for some time now to make ClickableBehavior 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 use ClickableBehavior to use Clickable instead.

@jeresig
Copy link
Member

jeresig commented Apr 19, 2019

Deploy preview for wonder-blocks ready!

Built with commit 41a5811

https://deploy-preview-387--wonder-blocks.netlify.com

@codecov
Copy link

codecov bot commented Apr 19, 2019

Codecov Report

Merging #387 into master will increase coverage by 0.04%.
The diff coverage is 100%.

Impacted file tree graph

@@            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
Impacted Files Coverage Δ
...onder-blocks-core/components/clickable-behavior.js 97.56% <ø> (ø) ⬆️
...es/wonder-blocks-clickable/components/clickable.js 100% <100%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9f9d5d7...41a5811. Read the comment docs.

Copy link
Member

@jeresig jeresig left a 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";
Copy link
Member

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.

Copy link
Member Author

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> {
Copy link
Member

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,
Copy link
Member

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>
);
Copy link
Member

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";
Copy link
Member

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 😃

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 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);
Copy link
Member

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?

Copy link
Member Author

@kevinbarabash kevinbarabash left a 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}
Copy link
Member Author

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: {
Copy link
Member Author

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.

Copy link
Contributor

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

Copy link
Member Author

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. 🤔 😅

Copy link
Member Author

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?

packages/wonder-blocks-clickable/components/clickable.js Outdated Show resolved Hide resolved
packages/wonder-blocks-clickable/package.json Show resolved Hide resolved
packages/wonder-blocks-clickable/package.json Outdated Show resolved Hide resolved
@@ -0,0 +1,39 @@
{
"name": "@khanacademy/wonder-blocks-clickable",
"version": "1.0.0",
Copy link
Member Author

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.

Copy link
Member

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?

Copy link
Member

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,
Copy link
Member Author

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) {
Copy link
Member

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?

Copy link
Contributor

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) {
Copy link
Member

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.

Copy link
Contributor

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 {
Copy link
Member

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.

Copy link
Contributor

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",
Copy link
Member

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

good idea!

Copy link
Contributor

@kumailn kumailn Jul 2, 2019

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 :/

Copy link
Member Author

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.

Copy link
Member

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

@kumailn kumailn requested a review from jeresig July 2, 2019 18:58
Copy link
Member Author

@kevinbarabash kevinbarabash left a 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",
Copy link
Member Author

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.

Copy link
Contributor

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={[
Copy link
Member Author

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?

Copy link
Member Author

@kevinbarabash kevinbarabash left a 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) =>
Copy link
Member Author

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
Copy link
Member Author

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.

Copy link
Member Author

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

Copy link
Member

@jandrade jandrade left a 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",
Copy link
Member

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 = {|
Copy link
Member

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.

Copy link
Contributor

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,
Copy link
Member

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

Copy link
Contributor

Choose a reason for hiding this comment

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

thanks for catching that!

Copy link
Member

@jandrade jandrade left a 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(
Copy link
Member

Choose a reason for hiding this comment

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

NIT: Missing // Arrange

Copy link
Contributor

Choose a reason for hiding this comment

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

good catch :)

Copy link
Member

@somewhatabstract somewhatabstract left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@jeresig jeresig left a comment

Choose a reason for hiding this comment

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

Excellent, thank you!

@kumailn kumailn merged commit 4cadfc8 into master Jul 9, 2019
@somewhatabstract somewhatabstract deleted the clickable branch October 2, 2019 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants