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

Component: Banner #9653

Merged
merged 25 commits into from
Dec 9, 2016
Merged

Component: Banner #9653

merged 25 commits into from
Dec 9, 2016

Conversation

Copons
Copy link
Contributor

@Copons Copons commented Nov 27, 2016

As proposed by @folletto, here's the new UpgradeBanner component, aiming to replace UpgradeNudge and all its custom variations.

Goals:

  • Keep the default as our current default
  • Allow for smaller sizes, as well as richer content (lists, images, buttons, prices).
  • Tweaked styling for mobile

Testing

http://calypso.localhost:3000/devdocs/blocks/upgrade-banners

Status

  • Button version
  • Compact card version
  • Default card version
  • Responsive
  • Text CTA
  • Button CTA
  • Lists
  • Custom colors
  • Custom icons
  • Analytics tracking
  • Dismiss button
  • Plan and feature props
  • Semantic colors
  • Plan prices
  • Plan icons

Screenshots

screen shot 2016-11-27 at 17 21 56

screen shot 2016-11-27 at 17 22 15

@matticbot
Copy link
Contributor

matticbot commented Nov 27, 2016

@folletto
Copy link
Contributor

folletto commented Nov 28, 2016

Design wise it's looking great so far! :)

Just a tiny addition: can we add these rules to handle a more evident hover state aligned with how we do things elsewhere (i.e. themes, reader):

.card.upgrade-banner:hover {
  transition: all 100ms ease-in-out;
}

.card.upgrade-banner.is-card-link:hover {
  box-shadow: 0 0 0 1px #87a6bc, 0 2px 4px #c8d7e1
}

I made the selector as CSS ones, but add them as you see fit in the SCSS.

Some smaller design details which I'm sure are already on your list, but just to be sure:

  1. The one-line banner should be slightly shorter (~54px for the one line, 64-67px for the two lines). I'm not for pixel perfection here, but the one liner is meant to use less space, so it's better if we can tweak it to be shorter.
  2. The two lines version needs some more spacing between the two lines (like a margin-top: 3px on the description).
  3. The description div shouldn't show if there's no description (this should make the fix to the above easy)
  4. The description should be $gray-dark too. We are starting to be more careful in accessibility (contrast).
  5. As the bullet list text (not the checkmarks which are fine in light gray).
  6. .upgrade-banner__action is a bit too far from the right chevron. A margin-right: -6px would do, or even just a 0 if we don't want to use negatives... or any other solution that you feel it works.

Ace work there! Thanks!

onClick();

if ( href ) {
window.location.href( href );
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this if the rendered <Button /> will be an anchor element anyways? If we do, we should at the very least call on page instead of window.location directly.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point: I'd also raise the issue that if there's a URL to point to, the link should always be a standard href= so it leverages of all the browser optimizations for it (open in a new tab, right click, drag'n'drop, etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry guys, this wasn't supposed to be still here. I'm gonna remove it ASAP.

} = this.props;

if ( event ) {
analytics.tracks.recordEvent( 'calypso_upgrade_banner_cta_click', {
Copy link
Contributor

Choose a reason for hiding this comment

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

Had you considered using the Redux analytics action creators? I could even see this hooking into the onClick prop, something like:

export default connect( null, ( dispatch, { onClick, event } ) => {
	if ( event ) {
		onClick = flow( 
			onClick,
			partial( recordGoogleEvent, 'calypso_upgrade_banner_cta_click', { cta_name: event } )
		);
	}

	return { onClick };
} )( UpgradeBanner );

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I pretty much copied it from the Card component without giving it much thought, and that's exactly why it's on the todo list. :)

</ul>
}
</div>
{ ! button &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Some docs for the component would be good, since it's not obvious to me why button wouldn't be an indicator for rendering the button (here, appears opposite?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Readme incoming. I was more interested in pushing to trigger some design related discussions. :)

<div className="upgrade-banner__description">
{ description }
</div>
{ list &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we check length (perhaps size( list ) > 0 using _.size) to avoid rendering the list if it's empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Considering that the purpose of this component is display some copy text, is it really something we should be worried about?
(Also, is there a reason to use _.size instead of Array.length?)

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm just imagining some case where we dynamically compose a list to pass to this component, and if that list happens to be empty, we might render this element, which I assume would have some base styling applied to push margins etc?

(Also, is there a reason to use _.size instead of Array.length?)

const list = undefined;

console.log( size( list ) );
// => 0

console.log( list.length ) );
// Uncaught TypeError: Cannot read property 'length' of undefined

}
}

bannerContent() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Usually I find if you're creating separate render methods, either (a) it should be inlined into the base render method or (b) it should be split into a separate component.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess a separate button component could make sense in this case, considering how many more props the card version has compared to the button one.
As the button version is pretty much ready, though, maybe I could include it here and split in a following PR?

@davewhitley
Copy link
Contributor

Looping in @rickybanister

Ties into the discussion around extending the nudge to other uses than upgrading

@mtias
Copy link
Member

mtias commented Nov 28, 2016

The way it is implemented at the moment, this seems like a component and not a block to me. The main benefit of the old upgrade nudge is that it abstracted a few things for you. For instance, you would pass a feature prop and it would build a standard url highlighting a plan row.

It also handled display logic for showing or not showing a nudge based on what features your current plan supports, which is very important.

Finally, I suggest looking at the cosmetic props (like color) and removing them in favor of the component handling it. If a feature belongs to a specific plan, the component would use the color from said plan, etc.

Same with price, it'd be good for the block to grab the price via a selector, because there are many intricacies there that should not be left up to be figured out by the person using one of these banners.

@Copons
Copy link
Contributor Author

Copons commented Nov 28, 2016

@folletto

The one-line banner should be slightly shorter (~54px for the one line, 64-67px for the two lines).

With the new margin-top: 3px; for the description, the two versions have the following heights:

  • Title only: 54px
  • Title + description: 62px

Do you think it's fine like this, or should I increase the two-liners vertical paddings?

@folletto
Copy link
Contributor

Do you think it's fine like this, or should I increase the two-liners vertical paddings?

Let's keep that. We can always tweak later. :)

@Copons
Copy link
Contributor Author

Copons commented Dec 4, 2016

I've added the Banner i3 version.
It makes the most of plans, with automatic colors and icons.

Colors: I've opted for the strictest approach.
Now the Banner is blue except when a plan is defined. In those cases, the Banner automatically takes the plan color (personal = yellow; premium = green; business = purple).
Custom colors are not allowed anymore.

Icons: icons are mandatory if no plan is defined. Otherwise, the icon defaults to the PlanIcon.

Testable at http://calypso.localhost:3000/devdocs/design/banner

@Copons Copons force-pushed the add/upgrade-banner-component branch from d088244 to a124772 Compare December 4, 2016 13:37
@Copons
Copy link
Contributor Author

Copons commented Dec 4, 2016

i3 screenshot:

screen shot 2016-12-04 at 15 11 54

@folletto
Copy link
Contributor

folletto commented Dec 5, 2016

I think we are almost there! :)

The only odd thing in the DevDocs is that the gridicons-info-outline default icon seems off centered. It's adjusted with .banner__icon-circle { padding: 3px 4px 4px 3px; ... }... can you check if it works also with other icons, just to be sure?

I'd also suggest to have a canonical example of each of the three plans banners in the showcase: identical, just with the text changed to match the plan and the same structure. So it's clear what changes from one plan to the other, and just that change. I'd thus then add a separate one that changes the icon from the default. To be clearer:

  1. Default compact banner (as it is now)
  2. Default banner with second line and changed icon
  3. "Upgrade to Personal Plan!"
  4. "Upgrade to Premium Plan!"
  5. "Upgrade to Business Plan!"
  6. The banner with everything — price and button combination.
  7. The banner with everything — close and button combination.

Makes sense?

@Copons
Copy link
Contributor Author

Copons commented Dec 5, 2016

@folletto It does, and yes, the icons seemed off to me too.
And not only the info-outline, which is more obvious since it's round as the background, but also the star (check the old screens) and the trophy, even though it's not as noticeable.

Gonna fix the icons and add the dismiss button asap!

@Copons
Copy link
Contributor Author

Copons commented Dec 5, 2016

New screenshot with all features added (cc @folletto)

screen shot 2016-12-05 at 17 17 22

All things considered, I stick with my proposal of having several sub-components (ie. DismissibleBanner, CtaBanner, etc. - the sky's the limit!) with limited options for ease of use, while still providing the main Banner for extremely customized uses.
Just see how many props and quirks there are in the README.

@Copons Copons added [Status] Needs Design Review Add this when you'd like to get a review / feedback from the Design team on your PR [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. and removed [Status] In Progress labels Dec 5, 2016
@folletto
Copy link
Contributor

folletto commented Dec 5, 2016

I did a test run, design wise 👍

@folletto folletto removed the [Status] Needs Design Review Add this when you'd like to get a review / feedback from the Design team on your PR label Dec 5, 2016
@Copons
Copy link
Contributor Author

Copons commented Dec 7, 2016

At this point I'd say we should review and ship this now and, if needs arise, extend it with specific sub-components at a later time.


static defaultProps = {
dismissTemporary: false,
feature: false,
Copy link
Contributor

Choose a reason for hiding this comment

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

If there's no reasonable default, we don't need to assign a defaultProps value. We could just not define any default and it would work just as well, leveraging truthiness of undefined prop in the condition where it's used. Also saves you the need to assign it as a oneOf option in propTypes.

@Copons Copons changed the title Component: UpgradeBanner Component: Banner Dec 8, 2016
Copons and others added 17 commits December 9, 2016 10:40
- Card version: add hover transitions and box shadows.
- Increase description top margin.
- Do not show empty description div if description is not provided.
- Darken description and list text color.
- Reduce banner action right margin.
- Use Redux analytics.
- Remove `href` leftover in `handleClick`.
- Add check on list size.
Add `getSelectedSiteSlug` selector to build the `href` prop pointing to
the pricing page and highlighting the requested feature.
- Reorder devdocs examples.
- Add default icon.
- Center icons in circles.
- Updated README.
@Copons Copons force-pushed the add/upgrade-banner-component branch from e8c1112 to 2486f02 Compare December 9, 2016 10:40
Add the missing style after this revert:
333edb53d812c3f424590ada
1a98717c35a277e1
Copy link
Contributor

@aduth aduth left a comment

Choose a reason for hiding this comment

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

Minor notes, but otherwise LGTM 👍

dismissPreferenceName: PropTypes.string,
dismissTemporary: PropTypes.bool,
event: PropTypes.string,
feature: PropTypes.oneOf( [ ...getValidFeatureKeys() ] ),
Copy link
Contributor

Choose a reason for hiding this comment

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

You can drop the spread:

feature: PropTypes.oneOf( getValidFeatureKeys() ),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Dang, you're totally right!

static defaultProps = {
dismissTemporary: false,
onClick: noop,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

ESLint does not warn yet, but static property and instance property initializers need semicolons.

https://github.com/tc39/proposal-class-public-fields/pull/45/files
https://tc39.github.io/proposal-class-public-fields/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Uh, I wasn't aware of this.
Gonna fix it and merge.

@aduth aduth added [Status] Ready to Merge and removed [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. labels Dec 9, 2016
- Dropped an unnecessary leftover spread.
- Added semicolons to static properties.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants