Upload
nicole-sullivan
View
972
Download
2
Tags:
Embed Size (px)
Citation preview
BUILDING THE MEDIA BLOCK IN REACTJS
the API is an interface that needs to be designed
https://www.flickr.com/photos/darpi/212323100/in/photolist-jLdg7-4zEiwa-nwFCR-cZVtBE-4NHtnv-7daytd-pce3y-4NH3fh-5TLS72-58aMX7-58aMVN-a3826y-gVDVKr-8DRhUB-nEdkNf-6tnApQ-fqJRqv-4NMHx7-7fUPoM-cNu2W-8etirE-o7PZA-wsB4L-7ABatu-8LyEF5-7iEjwY-3faCd1-9Gn1fF-4qYRms-JUKbE-7B97bb-69Uer5-5DQMzG-f4ZgsG-7TvUJp-5zyhEh-65naZh-nBkNs-5eCcAF-sErjv-4ePHGY-6kV1Q4-
nxWYX-dpGR5-55vgqd-5scyot-5t7uJJ-nLQn5y-9Njbu-79B4aw
@STUBBORNELLAtwitter really is the best way to reach me…
WHAT WILL WE COVER?
❖ What is a media block? ❖ Why use ReactJS ❖ Making a media block in ReactJS
WHAT IS THE MEDIA BLOCK?
let’s look at an example from Facebook
MEDIA BLOCK EXAMPLE
ALL OF THESE ARE THE SAME OBJECT
WHAT DO WE KNOW?
!❖ Can be nested ❖ Optional right button ❖ Must clearfix
WHAT DO WE KNOW?
!❖ Allows top, middle, or
bottom alignment
WHAT DON’T WE KNOW?
❖ Image width and decoration vary ❖ Right content is unknown ❖ Width unknown
A FEW LINES OF HTML...
<div class="media attribution"> <a href="http://twitter.com/stubbornella" class="img"> <img src="mini.jpg" alt="Stubbornella" /> </a> <div class="bd">@Stubbornella 14 minutes ago</div></div>
4 LINES OF CSS...
_
I <3 HTML, WHY USE REACT?
❖ html consistency (modal debugging) ❖ smaller api (fewer breaking changes) ❖ don’t need to go everywhere a component
was used to update the dom if it changes ❖ lightning fast dom updates when data
changes ❖ not managing those updates manually ❖ simpler APIs (tabs example)
I CAN’T COVER
EVERYTHINGSo do Ryan Florence’s
tutorials: https://github.com/
ryanflorence/react-training/
https://www.flickr.com/photos/wscullin/3770015991/in/photolist-6K9jpH-4mbNmG-4Cryx-6K9jC4-9hYhWK-dexZ3L-bpV8UA-6K9jb8-2aRBU9-c2mfZu-8desRa-ntKGaS-8ZxBba-7RVQRi-7AbLTM-dMTqW3-4866mH-rjNaZL-7MP9RU-dhfQ5N-rfCB9g-nBshnQ-7Xn7f-87asnj-84R7FU-sepy36-dtEmrp-rhsh7A-hVCY8u-4m7NmB-qqiNK6-bMLUPp-qY4LbS-q1prK-5WoP4f-Ftxe-4mbHVY-jB4t16-5qgm94-5WjwED-7vS6fV-5WoP4N-biVihz-9KsFUo-9KpQVF-9KpQMz-
r5vEd9-nkFqB2-4mbL7L-7VBztH
BUILDING THE MEDIA BLOCK IN REACT
APIs are an interface that needs to be designed
WHAT ARE THE ELEMENTS OF A REACT INTERFACE
❖ Elements ❖ Attributes ❖ Nested children ❖ Data
Sounds a lot like html, right?
HOW DO YOU MAKE A MEDIA COMPONENT?
in React
Codevar Media = React.createClass({ !});
SO THEN, HOW WOULD YOU USE IT?
It’s kind of like HTML, it’s called jsx
<Media> My very first React component! </Media>
BUT, WE NEED TO EXPORTthe Media component, so it can be used in other files
module.exports = { Media: Media };
DESCRIBE IT WITH A RENDER FUNCTIONThis tells the browser what HTML to render.
var Media = React.createClass({ render: function () { return ( <p> {this.props.children} </p> ); } });
just render a paragraph, for now
tell React where to render children
WHAT !
!
!
IS GENERATED?
https://www.flickr.com/photos/f-l-e-x/3096005116/in/photolist-5HzQsy-ah1UJ3-9WjzQW-q6xrU5-eQkJhu-oWah5L-noSbAV-9nNZaQ-5XZ3To-9gi7Rx-eMzU1y-9gb3m8-eQ8LqD-9WSkjF-9WSkhK-9zurtr-9WSk3X-eQkasL-eQ8GQ6-gi65Qe-9gbgVc-9geLWE-9gbiwR-k3TpbP-9EiwTi-bMTAYe-aYNGWB-6VTDDM-96t4ms-9s4Jas-bsjtmK-edG4Dq-rFb2Q-aZYKCH-Paes6-sd9Vtx-eg9fuW-9rYCnp-edgbX5-bmrzPv-9qA9iA-aMgUmz-s1AMwV-9zurqp-gXd9UH-
iEyyC-3NaTMa-bodPpW-dLeWjL-bxHrbz
LET’S TRY IT!Sweet, this is gonna be rad.
<Media> This text is our component’s children! </Media> !!!!<p> This text is our component’s children! </p>
LET’S TRY IT!Sweet, this is gonna be rad.
<Media> This text is our component’s children! </Media> !!!!<p> This text is our component’s children! </p>
When React renders that component we get this html
LET’S TRY IT!Sweet, this is gonna be rad.
<Media> This text is our component’s children! </Media> !!!!<p> This text is our component’s children! </p>
{this.props.children}
When React renders that component we get this html
NOT VERY USEFUL, RIGHT?You'd never use react to render a simple paragraph because it
already understands <p> tags, you can just use them!
LET’S BUILD AN ACTUAL MEDIA BLOCK
Step 1: Add the left image
SO, WHAT DID WE TRY FIRST?
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
TONS OF ATTRIBUTESWhat could go wrong? ;)
https://www.flickr.com/photos/ilo_oli/5856288807/in/photolist-jpnSsc-bp1mTF-iC3JNn-feu2hV-feJdEw-dCsd6V-aQmwUk-qJvwYx-qKHp7N-qKJtpL-pMFnAC-9XxWkr-qKJEV1-q6hte9-qZZXHG-q6hp8d-qKHchA-qKJt8U-bp1mv2-b6LSWT-bKfSon-kxWCNt-b6LNN8-b8Vpk6-ehScwd-c5CMgq-qKJcqw-r3iaXK-pMieGe-9ZUFNi-aD4zaE-fDwLBF-9Vv1CF-kzarYi-9ovVLy-rVLApS-fDwKki-b5sxAg-fskG7P-bUUme1-bKgdD6-c5CMiW-a1wsSP-4u7U5a-dtv95k-dtvKP6-
dtBtwJ-dtvrRR-dtBm2s-abcAqR
CREATE A SOURCE ATTRIBUTE
We’ll use it in our render function to set the src of the image
<Media leftImageSource='http://placehold.it/50x50'> Media block content </Media>
React props are a lot like HTML
attributes
BUT WAIT, THE IMAGE CAN BE A LINK
Sometimes…
SO, LET’S ADD AN HREF ATTRIBUTE
<Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com'> Media block content </Media>
Our render function will make this into a link wrapper (if it is set)
THE IMAGE CAN BE VERTICALLY ALIGNED
ADD AN ALIGNMENT ATTRIBUTE
<Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle'> Media block content </Media>
vertical alignment, could be top, middle or bottom
OH SHOOT, ACCESSIBILITY!
WE NEED AN ALT ATTRIBUTE
<Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle' leftImageAlt="profile photo"> Media block content </Media>
Our render function will make this into an alt attribute on the
<img> tag
OH, AND PERFORMANCE!
WE NEED A HEIGHT AND WIDTH
<Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle' leftImageAlt="profile photo" leftImageHeight="50px" leftImageWidth="50px"> Media block content </Media>
Explicit height and width allow the browser to do a single rendering/painting
pass
SET THE GUTTER BETWEEN IMAGE AND CONTENT
with the spacing attribute
<Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle' leftImageAlt="profile photo" leftImageHeight="50px" leftImageWidth="50px" leftImageSpacing="medium"> Media block content </Media>
For when the 10px default spacing isn’t right, like a tiny
icon with text
PROPERTIES OF THE MEDIArather than its images
FOR EXAMPLE, IT’S RESPONSIVE
via the stacksize (breakpoint) attribute
<Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle' leftImageAlt="profile photo" leftImageHeight="50px" leftImageWidth="50px" leftImageSpacing="medium" stackSize='medium'> Media block content </Media>
stack size puts the image above the text at the
breakpoint
THIS IS GETTING OUT OF HAND!
But we forgot something, the media block can have a right image
ADD THE RIGHT IMAGE PROPERTIES
This is out of control, we are going the wrong way!
<Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle' leftImageAlt="profile photo" leftImageHeight="50px" leftImageWidth="50px" rightImageSource='http://placehold.it/50x50' rightImageHref='http://www.google.com' rightImageAlignment='middle' rightImageAlt="profile photo" rightImageHeight="50px" rightImageWidth="50px" rightImageSpacing="medium" bodyAlignment='middle' stackSize='medium'> Media block content </Media>
right image props
PROS/CONS
❖ It’s very explicit, we know what each thing does
What works?❖ We're basically recreating
html in React, yuck! (we shouldn’t make a new different alt attribute!
❖ We have image properties and media properties all mixed up
❖ We have too many properties
What doesn’t work?
SO, WHAT DID WE TRY NEXT?
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
JSON ALL THE THINGS
https://www.flickr.com/photos/bike/4797449644/in/photolist-8iWbD3-5HPWhF-QyKg5-eVYyjt-3meRNg-5HPW94-4HwYMz-utah8-3qvXS-aqsJtx-eyQK4T-42P9p1-7Th995-5oKgDS-5mJEJe-9bt9At-4zbwwo-8vDr8Z-7konhy-BhrJ9-5zHQ7E-bobveq-DmrMg-3qvNs-5HPW1P-qtLJmp-5ZCPcW-9QuNBj-5HUdgS-9thCcq-6FmTKV-7QgAua-6DZyzu-gkukag-apwsgp-8hWccC-4U7EX6-pfaKPb-hvM3q-asXuSH-at18RL-asXuDV-asXuC4-9ys6M7-phFVSp-
dkdPkb-86toqn-dzVg-zVaLA-cDsK7N
OUR IMAGES AS JSONkinda weird, but it might work…
var images = [ { "src": "http://placehold.it/50x50", "href": "http://www.google.com", "alignment": "middle", "alt": "profile photo", "height": "50px", "width": "50px" }, { "src": "http://placehold.it/50x50", "href": "http://www.google.com", "alignment": "middle", "alt": "profile photo", "height": "50px", "width": "50px" } ];
JSON IN THE MEDIApassed in as another property
<Media images={images} bodyAlignment='middle' stackSize='medium'> Media block content </Media>
JSON IN THE MEDIApassed in as another property
<Media images={images} bodyAlignment='middle' stackSize='medium'> Media block content </Media>
{curly braces} mean it’s a JavaScript variable rather than a string
JSON IN THE MEDIApassed in as another property
<Media images={images} bodyAlignment='middle' stackSize='medium'> Media block content </Media>
json goes into the images attribute
{curly braces} mean it’s a JavaScript variable rather than a string
What works? What doesn’t work?
PROS/CONS
❖ abstraction of passing in JSON means all the code isn't in the same place
❖ weird to have JSON in the middle of what looks like markup
❖ still reinventing html attributes of an <img> tag
❖ cleaner separation of concerns (media takes care of media stuff, rather than the properties of its children)
WHAT DID WE DO NEXT?
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
PARSING CHILDRENWe decided to try including the images as children
https://www.flickr.com/photos/i-am-mclovin/16535518502/in/photolist-rcbRyA-nshV4n-eAaqTz-bCfUFZ-jH4tBF-pctQQD-qNmfZS-eT3GMZ-bTJsji-N8LkW-iCxgoA-7JDTp2-mPGu7V-dV4m7G-igpkaV-dRobZv-mnUN9i-igoYgJ-bCzBBi-f9tdxa-oMiWTE-b6LMzz-rcTY6S-dYq12b-qUh6hV-f6oFCx-pmwC9Z-hNLucH-moYnBt-6uGwja-aRrBm4-mPGGDB-igp6YC-f8b3QR-igpkXB-igoY3C-o62zzh-iC3JNn-9217QQ-D3JPG-pcHyUy-pprMfU-igoJAg-hgRxSL-pqomg9-
ahQDpD-4LkbKg-hNLcDy-igoJb8-9STs34
PARSING CHILDREN better, everything is normal html! But, it has a few drawbacks
<Media> <img src='http://placehold.it/50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width="50px" > <p>My media content</p> <img src='http://placehold.it/50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width="50px" > </Media>
What works? What doesn’t work?
PROS/CONS
❖ The images and body content need to be in a very particular order, it seems weird to expose that to the user
❖ Violates the "build components you can use without understanding CSS” principle
❖ Normal HTML ❖ Facebook does it this way
WHAT *ELSE* DOESN’T WORK?
❖ We could loop over children and reorder them, but how do we tell the difference between content images and media images?
❖ We were still discovering React, and didn't know how to loop over children yet
❖ React provides handy error messages and property validations. We would lose out on that if we made the images children
❖ Facebook's images aren't optional, so it's a different case
SO, WHAT DID WE TRY NEXT?
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
REACT BUILT-IN <IMG> COMPONENT
In react, everything is a component.
FIRST, WE MAKE OUR IMAGES
var leftImage = <img src='http://placehold.it/50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width="50px">; !var rightImage = <img src='http://placehold.it/50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width=“50px">;
NEXT, WE MAKE OUR MEDIA OBJECT
this looks similar to the JSON example
<Media leftImage={leftImage} rightImage={rightImage} bodyAlignment='middle' stackSize='medium'> Media block content </Media>
NEXT, WE MAKE OUR MEDIA OBJECT
this looks similar to the JSON example
<Media leftImage={leftImage} rightImage={rightImage} bodyAlignment='middle' stackSize='medium'> Media block content </Media>
left and right images are passed into attributes
YOU CAN EVEN WRITE ITlike this if you really want to
<Media leftImage={<img src='http://placehold.it/50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width="50px">} bodyAlignment='middle' stackSize='medium'> Media block content </Media>
image component directly in the attribute property
What works? What doesn’t work?
PROS/CONS
❖ HTML inside an attribute (in the latter example) is a bit odd, though it does have advantages.
❖ React passes default html attributes in to the resulting img tag, so we don't have to do anything special with height, width, src, aria and alt.
❖ We separate concerns and the image takes care of it's own properties
❖ No need to parse content
WHAT *ELSE* DOESN’T WORK?
❖ href will be passed through. So our image will have an href attribute. I like clean html, and that feels weird to me!
<div class="media"> <a href="styleguide.pivotal.io"> <img href="styleguide.pivotal.io" /> ...
Yuck!
WE CONSIDERED GOING BACK TO PROPERTIES…
but decided we should make our own <img> wrapper
<Media leftImageHref="styleguide.pivotal.io">
to property or not to property?
SO, WHAT DID WE TRY NEXT?
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
CUSTOM IMAGE COMPONENT
And another component was born…https://www.flickr.com/photos/eltonharding/522073772/in/photolist-N8LkW-dLnt39-dsdYRQ-qcf3KQ-rgojpc-dsdWxA-dRxje9-iAgtmB-aaTYBU-mgGFrk-d3TMAf-opZhPw-nbi6ut-gMDt1W-adGAGZ-j8HqHK-gAt6ec-mc944B-nEMWpG-oQkVQ4-qR9xvi-gSVfC5-oUurRb-9jGTJD-nWUZza-e5NeHJ-aTYLCT-dTTRha-rp3zLC-qn7i8t-hQxDrG-9qcih5-sn5TTi-9aQfXm-nsgfeC-niFxPL-dRxjy7-9Ry7C3-p8VRa4-noA5cx-oovJdV-kLSLxL-dpgFWM-rhZ9Ri-
dRxkm3-9qResk-kGDeJb-bprRNw-oCC5tt-oCX7iY
GOAL: OUTPUTS A SIMPLE <IMG> TAG
but won't pass through attributes that don't make sense like href
STEP 1: CREATE THE IMAGE COMPONENT
var Image = React.createClass({ !});
STEP 2: EXPORT THE IMAGEthe same way we did the Media component
module.exports = {Image};
STEP 3: GET ITS PROPERTIESand render an image
var Image = React.createClass({ render() { var {href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src} className={classes}>; ! return href ? <a {...{href}}>{image}</a> : image; } });
STEP 3: GET ITS PROPERTIESand render an image
var Image = React.createClass({ render() { var {href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src} className={classes}>; ! return href ? <a {...{href}}>{image}</a> : image; } });
get the properties we need
STEP 3: GET ITS PROPERTIESand render an image
var Image = React.createClass({ render() { var {href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src} className={classes}>; ! return href ? <a {...{href}}>{image}</a> : image; } });
get the properties we need
build the image from our properties
STEP 3: GET ITS PROPERTIESand render an image
var Image = React.createClass({ render() { var {href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src} className={classes}>; ! return href ? <a {...{href}}>{image}</a> : image; } });
get the properties we need
build the image from our properties
if we have a link, wrap the image in an <a> tag
STEP 4: MAKE IT RESPONSIVE
by handling the responsive boolean
var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src}>; return href ? <a {...{href}}>{image}</a> : image; } });
STEP 4: MAKE IT RESPONSIVE
by handling the responsive boolean
var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src}>; return href ? <a {...{href}}>{image}</a> : image; } });
get the responsive property
STEP 4: MAKE IT RESPONSIVE
and setting the img-responsive class if the boolean is true
var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var classes = classnames({'img-responsive': responsive}, className); !!!!!!! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
STEP 4: MAKE IT RESPONSIVE
and setting the img-responsive class if the boolean is true
var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var classes = classnames({'img-responsive': responsive}, className); !!!!!!! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
add this classif this evaluates
to true
STEP 4: MAKE IT RESPONSIVE
and setting the img-responsive class if the boolean is true
var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var classes = classnames({'img-responsive': responsive}, className); !!!!!!! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
add this classif this evaluates
to true
then, put the class on the image
STEP 5: VALIDATE PROPERTIES
var Image = React.createClass({ propTypes: { responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
STEP 5: VALIDATE PROPERTIES
var Image = React.createClass({ propTypes: { responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
responsive has to be true or false
STEP 5: VALIDATE PROPERTIES
var Image = React.createClass({ propTypes: { responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
STEP 5: VALIDATE PROPERTIES
var Image = React.createClass({ propTypes: { responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
the href is a string
STEP 5: VALIDATE PROPERTIES
var Image = React.createClass({ propTypes: { responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
STEP 5: VALIDATE PROPERTIES
var Image = React.createClass({ propTypes: { responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
src is a string and required
STEP 5: VALIDATE PROPERTIES
var Image = React.createClass({ propTypes: { responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
STEP 5: VALIDATE PROPERTIES
var Image = React.createClass({ propTypes: { responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
OUR “AH-HA” MOMENTUsers are still needing to specify too many things to get this
component to work, they might as well just write html!
SO, WHAT DID WE DO NEXT?
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
https://www.flickr.com/photos/mstewartphotography/6195718653/in/photolist-aruFip-3Ym2sP-d5ipmU-61ivfM-2SmfSo-eMWyyv-7Tx3yN-p7xVZN-5LExX5-dG1RK2-86LsMW-6TDV5Y-9isMj1-cJuUm-4v2NE-amKvge-61TfaY-61PcSp-61nGEd-86FasK-qrz8VS-4bGoKp-77DPsN-7NeZKn-botCkn-8vpoEE-oTAKWV-5Sz1Fv-a5oF29-86Hf14-aS9vmZ-a8M5q6-a8Mh8k-7AKzoA-86HcPz-61HM17-7AFRcK-a8LXht-5c5LvE-a8LZXX-61Hb8A-
fJgHuw-86LhDq-86Lmch-3jzdZ-5mYENE-8UMWoM-bx1iUr-p9jsSa-a865Ji
ELEMENTS FTWwe can simplify our interface further
https://www.flickr.com/photos/rejik/14681743931/in/photolist-onnLY4-nPYfhm-ed6PWM-bvWmjA-mMGE1V-j88ToM-ngTbpk-nUg38a-9n1hgv-4KZr2Z-nucMef-dd5exw-9eyaqy-8QWK1i-eaTuFL-4RbvFX-7kiwo3-7NqP2a-4R1KYB-mZEx1J-5iV12q-39v9f8-bqt2rx-7zvWs-9eyamJ-7JKZAh-hiwiDa-poG8fx-ehZRGj-684GeT-pPeQGL-efRP9f-icXKJY-aNxWqT-9niyKk-ouarpw-bmC5SK-7s5DNV-bqt3F8-bqsZ24-mZCLWp-86YqXk-e6ERub-bqtEL8-8K3pJf-
kik4tg-8yYivi-8fi3Ep-dVohpu-fzmggH
https://www.flickr.com/photos/rejik/14681743931/in/photolist-onnLY4-nPYfhm-ed6PWM-bvWmjA-mMGE1V-j88ToM-ngTbpk-nUg38a-9n1hgv-4KZr2Z-nucMef-dd5exw-9eyaqy-8QWK1i-eaTuFL-4RbvFX-7kiwo3-7NqP2a-4R1KYB-mZEx1J-5iV12q-39v9f8-bqt2rx-7zvWs-9eyamJ-7JKZAh-hiwiDa-poG8fx-ehZRGj-684GeT-pPeQGL-efRP9f-icXKJY-aNxWqT-9niyKk-ouarpw-bmC5SK-7s5DNV-bqt3F8-bqsZ24-mZCLWp-86YqXk-e6ERub-bqtEL8-8K3pJf-
kik4tg-8yYivi-8fi3Ep-dVohpu-fzmggH
DESIGNERS ONLY USE 2 KINDS OF ALIGNMENT
❖ Traditional media with everything top aligned !
!
!
❖ “Flag” component a la Harry Roberts middle aligns
WE MADE THESE TWO USE CASES DEAD SIMPLE
❖ We changed the media component to default to top alignment if nothing else was specified.
❖ We created the <flag> component
<Flag leftImage={refreshImage}> refresh </Flag>
What works? What doesn’t work?
PROS/CONS
❖ engineers don't always know what the flag object is, documentation and teaching help
❖ with Flag and Media, we no longer need to specify alignment unless we want something weird
ARE ANY OF THESE WRONG?
No, absolutely not.
https://gist.github.com/stubbornella/e97662e4a197eb9a534a
CHARTSloop over children
https://www.flickr.com/photos/sidereal/3326112973/in/photolist-AKC4i-9YVVYN-6EiwN6-GadYk-a67Nur-5VXHT1-Fv7K6-aozvYE-6fp6gc-6FzLjQ-fKvtt-2JGK9-bqeuyX-3jcvLj-Ljyy3-6kUXKM-bVyHMC-4dDwYk-5CGecv-6DUvgk-64Vcvp-8cK4x4-6b5yLD-jDwYG-wnfrN-vdeo8-8343Sa-dRCSgx-83wSzz-5xtVxe-5xyrZd-5VDSw6-
fXTYR-82Dnu2-5VDSwa-7MNruk-81biBH-5e3irn-5e3i4T-6mxH7A-5m93Dg-5pi6WC-5uvUeA-6n89sp-c465FQ-6SDa5S-5AbJnS-cN17Zf-KGVTi-7yvvmH
var sortableCols = [ { name: 'name', title: 'Name', sortable: true }, { name: 'instances', title: 'Instances', sortable: true, align: 'center' }, { name: 'cpu', title: 'CPU', sortable: true, align: 'right' }, { name: 'synergy', title: 'Synergy', align: 'left' } ]
TABS IN BOOTSTRAP<div role="tabpanel"> ! <!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class=“active"> <a href="#home" aria-controls="home" role="tab" data-toggle=“tab">Home</a> </li> <li role=“presentation"> <a href="#profile" aria-controls="profile" role="tab" data-toggle=“tab”> Profile</a> </li> </ul> ! <!-- Tab panes --> <div class="tab-content"> <div role="tabpanel" class="tab-pane active" id="home">...</div> <div role="tabpanel" class="tab-pane" id="profile">...</div> </div> !</div>
TABS IN REACT
<SimpleTabs defaultActiveKey={1}> <Tab eventKey={1} tab="Home"> ... </Tab> <Tab eventKey={2} tab="Profile"> ... </Tab> </SimpleTabs>
https://www.flickr.com/photos/wscullin/3770015991/in/photolist-6K9jpH-4mbNmG-4Cryx-6K9jC4-9hYhWK-dexZ3L-bpV8UA-6K9jb8-2aRBU9-c2mfZu-8desRa-ntKGaS-8ZxBba-7RVQRi-7AbLTM-dMTqW3-4866mH-rjNaZL-7MP9RU-dhfQ5N-rfCB9g-nBshnQ-7Xn7f-87asnj-84R7FU-sepy36-dtEmrp-rhsh7A-hVCY8u-4m7NmB-qqiNK6-bMLUPp-qY4LbS-q1prK-5WoP4f-Ftxe-4mbHVY-jB4t16-5qgm94-5WjwED-7vS6fV-5WoP4N-biVihz-9KsFUo-9KpQVF-9KpQMz-
r5vEd9-nkFqB2-4mbL7L-7VBztH
IT’S A DESIGN DECISION each component is different
KEEP LOOPING BACKchange the interface until it works well
GOOD DESIGN PRINCIPLES
❖ Many drawers - Tom O ❖ Set good defaults ❖ User shouldn’t need to understand CSS to use it ❖ Make tiny components with one job (same as CSS) ❖ Allow flexibility ❖ Prefer a complex implementation over a complex interface
what has worked for us
WHO ARE YOUR USERS?component creators and maintainers, contributors, developers
building features, actual product users
https://www.flickr.com/photos/fabiansociety/16300828766/in/photolist-qQs1tQ-qAa8pJ-pVJmYw-qxNcH4-qAaDuJ-qSHJsr-5SDe5H-josG7R-dxrFDm-e6S4TN-fddCLi-po7JuN-d21PZN-ax7LAK-qBLEie-dEMphp-byfU17-nPjAPc-eZ7ooX-ctHbf5-g5QFS-naVVhZ-cFgo6s-akEb2Q-qUQi3c-aGJ83i-627cGv-aRFFNx-nSyXpr-dyXFU7-aupkvk-buYgB2-nj7Xyv-jHSXR5-9eAqzK-eNqYdm-a4GaUk-qiFrdF-dy1QsG-bPqzrk-9dEUm7-n7cmgE-gJNeKz-nigszh-mi4QjT-s76Yxa-
pfkSRX-96twBn-6jrzx2-oAFqvC
USE ALL THE TOOLS IN YOUR TOOLBOX
❖ Elements (built in and custom) ❖ Attributes (simple and objects) ❖ JSON ❖ Children
styleguide.pivotal.io
WE OPEN SOURCED EVERYTHING
Please do give us feedback npm: https://www.npmjs.com/search?q=pui
github: github.com/pivotal-cf/pivotal-ui
THANK YOU!@stubbornella