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

Components: header cake cleanup #1514

Merged
merged 3 commits into from
Jan 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions client/components/header-cake/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
Back Button aka Header Cake
===========================

The "header cake" component should be used at the top of an item's detail page. It's purpose is to display a title and back link.

## Usage

```
```js
var HeaderCake = require( 'components/header-cake' );

<HeaderCake onClick={ callback }>Button Text</HeaderCake>
<HeaderCake onClick={ callback }>Item Details</HeaderCake>
```

## Props

* `onClick` - Function to trigger when the back text is clicked
* `onClick` - (**required**) Function to trigger when the back text is clicked
* `onTitleClick` - Function to trigger when the title is clicked
* `backText` - React Element or string to use in place of default "Back" text
* `isCompact` - Optional variant of a more visually compact header cake
61 changes: 61 additions & 0 deletions client/components/header-cake/back.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* External dependencies
*/
import React, { PropTypes } from 'react';
import classNames from 'classnames';

/**
* Internal dependencies
*/
import ObserveWindowSizeMixin from 'lib/mixins/observe-window-resize';
import Gridicon from 'components/gridicon';

/**
* Module variables
*/
const HIDE_BACK_CRITERIA = {
windowWidth: 480,
characterLength: 8
};

export default React.createClass( {

displayName: 'HeaderCakeBack',

mixins: [ ObserveWindowSizeMixin ],

propTypes: {
onClick: PropTypes.func,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this optional? If not we can add a PropTypes.func.isRequired here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this particular case it is actually optional because of the spacer usage. In order to give the title the most about of space possible and still keep in centered, the corners were changed to display the same text in both places (the right one is hidden). The right corner still uses the back component, but doesn't include any onClick props.

href: PropTypes.string,
text: PropTypes.string,
spacer: PropTypes.bool
},

getDefaultProps() {
return {
spacer: false
};
},

render() {
const { text = this.translate( 'Back' ), href, onClick, spacer } = this.props;
const windowWidth = window.innerWidth;
const hideText = windowWidth <= HIDE_BACK_CRITERIA.windowWidth && text.length >= HIDE_BACK_CRITERIA.characterLength || windowWidth <= 300;
const linkClasses = classNames( {
'header-cake__back': true,
'is-spacer': spacer
} );

return (
<a className={ linkClasses } href={ href } onClick={ onClick }>
<Gridicon icon="chevron-left" size={ 18 } />
{ ! hideText && <span className="header-cake__back-text">{ text }</span> }
</a>
);
},

onWindowResize() {
this.forceUpdate();
}

} );
41 changes: 18 additions & 23 deletions client/components/header-cake/index.jsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
/**
* External dependencies
*/
var React = require( 'react' ),
classNames = require( 'classnames' );
import React, { PropTypes } from 'react';
import classNames from 'classnames';

/**
* Internal dependencies
*/
var Card = require( 'components/card' ),
Gridicon = require( 'components/gridicon' );
import Card from 'components/card';
import HeaderCakeBack from './back';

export default React.createClass( {

module.exports = React.createClass( {
displayName: 'HeaderCake',

propTypes: {
onClick: React.PropTypes.func.isRequired,
onTitleClick: React.PropTypes.func,
backText: React.PropTypes.oneOfType( [
React.PropTypes.element,
React.PropTypes.string
] )
onClick: PropTypes.func.isRequired,
onTitleClick: PropTypes.func,
backText: PropTypes.string
},

getDefaultProps: function() {
getDefaultProps() {
return {
isCompact: false
};
},

render: function() {
var classes = classNames(
render() {
const { backText } = this.props;
const classes = classNames(
'header-cake',
this.props.className,
{
Expand All @@ -39,17 +38,13 @@ module.exports = React.createClass( {

return (
<Card className={ classes }>
<div className="header-cake__corner">
<a className="header-cake__back" onClick={ this.props.onClick }>
<Gridicon icon="chevron-left" size={ 16 } />
<span className="header-cake__back-text">{ this.props.backText || this.translate( 'Back' ) }</span>
</a>
</div>
<span className="header-cake__title" onClick={ this.props.onTitleClick }>
<HeaderCakeBack text={ backText } onClick={ this.props.onClick } />
<div className="header-cake__title" onClick={ this.props.onTitleClick }>
{ this.props.children }
</span>
<div className="header-cake__corner" />
</div>
<HeaderCakeBack text={ backText } spacer />
</Card>
);
}

} );
47 changes: 28 additions & 19 deletions client/components/header-cake/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,56 @@

.header-cake.card {
display: flex;
align-items: stretch;
justify-content: center;
align-items: center;
font-size: 14px;
line-height: 18px;
padding: 16px;
padding: 0;

&::after {
display: none;
}

@include breakpoint( "<660px" ) {
margin-top: 10px;
}
}

.header-cake__title {
color: darken( $gray, 20% );
text-align: center;
word-break: break-word;
flex: 2 1;
}

.header-cake__corner {
display: flex;
flex: 1 0;
}

.header-cake__back {
flex: 1 0;
flex: none;
display: block;
margin: -16px;
margin-right: 0;
padding: 15px 16px 16px;
max-width: 33.333%;
padding: 16px;
color: darken( $gray, 20% );
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;

.gridicon {
display: inline-block;
vertical-align: middle;
opacity: 0.6;
}

&.is-spacer {
opacity: 0;
cursor: default;
}
}

.header-cake__back-text {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
}

.header-cake__title {
flex: 1 1 auto;
padding: 16px 0;
color: darken( $gray, 20% );
text-align: center;
word-break: break-word;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ const DomainManagementHeader = React.createClass( {
render() {
return (
<HeaderCake className="domain-management-header" onClick={ this.props.onClick }>
{ this.domainName() }
<span className="domain-management-header__children">
{ this.props.children }
</span>
<div className="domain-management-header__children">
{ this.domainName() }
<span className="domain-management-header__title">
{ this.props.children }
</span>
</div>
</HeaderCake>
);
},
Expand Down
9 changes: 8 additions & 1 deletion client/my-sites/upgrades/domain-management/style.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
.domain-main-placeholder {
.header-cake__title {
.domain-management-header__children {
@include placeholder(23%);

@include breakpoint('>480px') {
max-width: 60%;
margin: 0 auto;
}
}
}

Expand Down Expand Up @@ -47,6 +52,8 @@

.domain-management-header__children {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.domain-management-list-item__link {
Expand Down
24 changes: 6 additions & 18 deletions client/post-editor/media-modal/detail/_style.scss
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
.editor-media-modal-detail .header-cake.card,
.editor-media-modal-detail .header-cake__corner {
display: block;

.editor-media-modal-detail .header-cake.card {
@include breakpoint( "<660px" ) {
margin-bottom: 0;
}
}

.editor-media-modal-detail .header-cake__back {
display: inline-block;
.editor-media-modal-detail .header-cake__title {
padding: 6px 0;
}

.editor-media-modal-detail__title .editor-media-modal-detail__title-input {
@extend .header-cake__title;
position: absolute;
top: 50%;
left: 50%;
transform: translate( -50%, -50% );
width: 200px;
width: calc( 100vw - 175px );
.editor-media-modal-detail .editor-media-modal-detail__title-input {
width: 100%;
color: inherit;
word-break: normal;
white-space: nowrap;
overflow: hidden;
Expand All @@ -27,10 +19,6 @@
outline: none;
text-align: center;

@include breakpoint( ">480px" ) {
width: 60%;
}

&[readonly]:hover,
&[readonly]:focus {
box-shadow: none;
Expand Down
22 changes: 10 additions & 12 deletions client/post-editor/media-modal/detail/detail-title.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,16 @@ export default React.createClass( {

render() {
return (
<h2 className="editor-media-modal-detail__title">
<TrackInputChanges onNewValue={ this.bumpStat }>
<FormTextInput
onKeyUp={ this.onKeyUp }
onChange={ this.onChange }
onBlur={ this.saveTitle }
value={ this.getTitleValue() }
placeholder={ this.translate( 'Untitled' ) }
readOnly={ ! userCan( 'upload_files', this.props.site ) }
className="editor-media-modal-detail__title-input" />
</TrackInputChanges>
</h2>
<TrackInputChanges onNewValue={ this.bumpStat }>
<FormTextInput
onKeyUp={ this.onKeyUp }
onChange={ this.onChange }
onBlur={ this.saveTitle }
value={ this.getTitleValue() }
placeholder={ this.translate( 'Untitled' ) }
readOnly={ ! userCan( 'upload_files', this.props.site ) }
className="editor-media-modal-detail__title-input" />
</TrackInputChanges>
);
}
} );
3 changes: 1 addition & 2 deletions client/post-editor/media-modal/detail/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ var React = require( 'react' ),
var DetailItem = require( './detail-item' ),
MediaUtils = require( 'lib/media/utils' ),
HeaderCake = require( 'components/header-cake' ),
BackToLibrary = require( '../back-to-library' ),
EditorMediaModalDetailTitle = require( './detail-title' ),
preloadImage = require( '../preload-image' ),
ModalViews = require( '../constants' ).Views;
Expand Down Expand Up @@ -65,7 +64,7 @@ module.exports = React.createClass( {

return (
<div className="editor-media-modal-detail">
<HeaderCake onClick={ this.returnToList } backText={ <BackToLibrary /> }>
<HeaderCake onClick={ this.returnToList } backText={ this.translate( 'Media Library' ) }>
<EditorMediaModalDetailTitle
site={ this.props.site }
item={ items[ this.props.selectedIndex ] } />
Expand Down
3 changes: 1 addition & 2 deletions client/post-editor/media-modal/gallery/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import isEqual from 'lodash/lang/isEqual';
* Internal dependencies
*/
import HeaderCake from 'components/header-cake';
import BackToLibrary from '../back-to-library';
import MediaStore from 'lib/media/store';
import EditorMediaModalGalleryDropZone from './drop-zone';
import EditorMediaModalGalleryFields from './fields';
Expand Down Expand Up @@ -132,7 +131,7 @@ export default React.createClass( {
<EditorMediaModalGalleryDropZone
site={ site }
onInvalidItemAdded={ () => this.setState( { invalidItemDropped: true } ) } />
<HeaderCake onClick={ this.returnToList } backText={ <BackToLibrary /> } />
<HeaderCake onClick={ this.returnToList } backText={ this.translate( 'Media Library' ) } />
<div className="editor-media-modal-gallery__content editor-media-modal__content">
<EditorMediaModalGalleryPreview
site={ site }
Expand Down