-
-
Save stubbornella/e97662e4a197eb9a534a to your computer and use it in GitHub Desktop.
'use strict'; | |
var React = require('react/addons'); | |
var _ = require('lodash'); | |
var setClass = React.addons.classSet; | |
var MediaObject = React.createClass({ | |
render: function () { | |
var classes = setClass({ | |
'media-left': this.props.horizontalAlignment === 'left', | |
'media-right': this.props.horizontalAlignment === 'right', | |
'media-middle': this.props.verticalAlignment === 'middle', | |
'media-bottom': this.props.verticalAlignment === 'bottom', | |
'media-object': !this.props.imageHref | |
}); | |
var paddingClasses = setClass({ | |
'prs': this.props.leftMediaSpacing === 'small', | |
'prm': this.props.leftMediaSpacing === 'medium', | |
'prl': this.props.leftMediaSpacing === 'large', | |
'prxl': this.props.leftMediaSpacing === 'xlarge', | |
'pls': this.props.rightMediaSpacing === 'small', | |
'plm': this.props.rightMediaSpacing === 'medium', | |
'pll': this.props.rightMediaSpacing === 'large', | |
'plxl': this.props.rightMediaSpacing === 'xlarge' | |
}); | |
var mediaClasses = [classes, paddingClasses].join(' '); | |
if (this.props.imageHref) { | |
return ( | |
<a className={mediaClasses} href={this.props.imageHref} > | |
<img alt={this.props.alt} className="media-object" src={this.props.imageSource} height={this.props.height} width={this.props.width} /> | |
</a> | |
); | |
} else { | |
return ( | |
<img alt={this.props.alt} className={mediaClasses} src={this.props.imageSource} height={this.props.height} width={this.props.width} /> | |
); | |
} | |
} | |
}); | |
var Media = React.createClass({ | |
render: function () { | |
var leftMedia, | |
rightMedia = ''; | |
var classes = setClass({ | |
'media': true, | |
'media-stackable-xs': this.props.stackSize === 'xsmall', | |
'media-stackable-sm': this.props.stackSize === 'small', | |
'media-stackable-md': this.props.stackSize === 'medium', | |
'media-stackable-lg': this.props.stackSize === 'large' | |
}); | |
var bodyClasses = setClass({ | |
'media-body': true, | |
'media-middle': this.props.bodyAlignment === 'middle', | |
'media-bottom': this.props.bodyAlignment === 'bottom' | |
}); | |
if (this.props.leftImageSource) { | |
leftMedia = ( | |
<MediaObject | |
horizontalAlignment='left' | |
verticalAlignment={this.props.leftImageAlignment} | |
imageHref={this.props.leftImageHref} | |
imageSource={this.props.leftImageSource} | |
leftMediaSpacing={this.props.leftMediaSpacing} | |
alt={this.props.leftAlt} | |
height={this.props.leftImageHeight} | |
width={this.props.leftImageWidth}> | |
</MediaObject> | |
); | |
} | |
if (this.props.rightImageSource) { | |
rightMedia = ( | |
<MediaObject | |
horizontalAlignment='right' | |
verticalAlignment={this.props.rightImageAlignment} | |
imageHref={this.props.rightImageHref} | |
imageSource={this.props.rightImageSource} | |
rightMediaSpacing={this.props.rightMediaSpacing} | |
alt={this.props.rightAlt} | |
height={this.props.rightImageHeight} | |
width={this.props.rightImageWidth}> | |
</MediaObject> | |
); | |
} | |
return ( | |
<div {...this.props} className={classes}> | |
{leftMedia} | |
<div className={bodyClasses}> | |
{this.props.children} | |
</div> | |
{rightMedia} | |
</div> | |
); | |
} | |
}); | |
module.exports = { | |
Media: Media | |
}; |
Here is where we've ended up...
- We created a flag object to handle middle vertical alignment, it just passes through to media and sets the right property. Media handles top alignment by default.
- We pass in the image as a param, which feels much cleaner
- We were able to add some validations
'use strict';
var React = require('react/addons');
var _ = require('lodash');
var setClass = React.addons.classSet;
var MediaObject = React.createClass({
render: function () {
var classes = setClass({
'media-left': this.props.horizontalAlignment === 'left',
'media-right': this.props.horizontalAlignment === 'right',
'media-middle': this.props.vAlign === 'middle',
'media-bottom': this.props.vAlign === 'bottom'
});
var paddingClasses = setClass({
'prs': this.props.leftMediaSpacing === 'small',
'prm': this.props.leftMediaSpacing === 'medium',
'prl': this.props.leftMediaSpacing === 'large',
'prxl': this.props.leftMediaSpacing === 'xlarge',
'pls': this.props.rightMediaSpacing === 'small',
'plm': this.props.rightMediaSpacing === 'medium',
'pll': this.props.rightMediaSpacing === 'large',
'plxl': this.props.rightMediaSpacing === 'xlarge'
});
var mediaClasses = [classes, paddingClasses].join(' ');
return (
<div className={mediaClasses}>
{this.props.children}
</div>
);
}
});
var Media = React.createClass({
propTypes: {
stackSize: React.PropTypes.oneOf(["xsmall", "small", "medium", "large"]),
vAlign: React.PropTypes.oneOf(["middle","bottom"]),
leftImage: function(props, propName, componentName) {
if(props[propName] && (!props[propName].type || props[propName].type !== "img") ) {
return new Error("Left image must be an image")
}
},
rightImage: function(props, propName, componentName) {
if(props[propName] && (!props[propName].type || props[propName].type !== "img") ) {
return new Error("Right image must be an image")
}
},
hasImages: function(props, propName, componentName) {
if(!props["leftImage"] && !props["rightImage"]) {
return new Error("The media component must have at least one image")
}
}
},
render: function () {
var leftMedia,
rightMedia = '';
var classes = setClass({
'media': true,
'media-stackable-xs': this.props.stackSize === 'xsmall',
'media-stackable-sm': this.props.stackSize === 'small',
'media-stackable-md': this.props.stackSize === 'medium',
'media-stackable-lg': this.props.stackSize === 'large'
});
var bodyClasses = setClass({
'media-body': true,
'media-middle': this.props.vAlign === 'middle',
'media-bottom': this.props.vAlign === 'bottom'
});
if (this.props.leftImage) {
leftMedia = (
<MediaObject
horizontalAlignment='left'
vAlign={this.props.vAlign}
leftMediaSpacing={this.props.leftMediaSpacing}>
{this.props.leftImage}
</MediaObject>
);
}
if (this.props.rightImage) {
rightMedia = (
<MediaObject
horizontalAlignment='right'
vAlign={this.props.vAlign}
rightMediaSpacing={this.props.rightMediaSpacing}>
{this.props.rightImage}
</MediaObject>
);
}
return (
<div {...this.props} className={classes}>
{leftMedia}
<div className={bodyClasses}>
{this.props.children}
</div>
{rightMedia}
</div>
);
}
});
var Flag = React.createClass({
getDefaultProps: function () {
return {
vAlign: 'middle'
}
},
render: function () {
return (
<Media {...this.props}>{this.props.children}</Media>
);
}
});
module.exports = {
Media: Media,
Flag: Flag
};
We still want to set it up to use a <Image />
component rather than the default html img (because it is weird to have an href attribute on a base html element, but that will have to be done after the holidays.
We think the validations will be simpler with an <Image />
component, something like:
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.instanceOf(Image),
React.PropTypes.instanceOf(Svg),
React.PropTypes.instanceOf(Icon)
]),
But yeah, that will have to wait...
To check if a ReactElement has a particular type, check its .type
property:
var img = <Image />;
img.type === Image // true
There's no built-in PropTypes validator to check for an element with a particular type.
You need to be really careful when checking against the type
field of a ReactElement. When done the naive way, it prevents you from writing abstractions.
For example, if you only accept a prop img
that satisfies img.type === Image
, then it only accepts a literal <Image ...>
component. But it won't accept a higher level component like <ProfilePicture>
that eventually renders an <Image>
component.
@spicyj @vjeux I'm having difficulty getting a ReactElement to return its type the way you specified... my reproduction produced the following:
var img = <Image />
img.type === Image // false
console.log(img.type)
outputs...
function (props) {
// This constructor is overridden by mocks. The argument is used
// by mocks to assert on what gets mounted. This will later be used
// by the stand-alone class implementation.
}
which comes from ReactClass.js. Would love any feedback to clarify my understanding of how to use the type
field of ReactElements!
Thanks @yungsters, of course I remember the ImageBlock, though it was written in xhp back when I was at facebook. ;)