NOTE: this was branched out of #26, as of its creation still not merged, because it requires the webpack changes from there.
Overview
Any piece of layout code that can potentially be shared between pages, or even repeated inside the same page, should go in its own BesugoComponent in a .jsx file. This way, we won't have to copy-paste repeating layouts in every file, or even rewrite it for CMS previews.
BesugoComponent
A Class extension of React.Component, what matters most to us is its ability to parse JSX from any data supplied to it, and that it can reuse other components when rendering itself.
All such components should have the following basic structure to be built:
import React from 'react';
import BesugoComponent from 'Besugo';
class SomeThing extends BesugoComponent {
// ...
}
SomeThing.initialize();
export default SomeThing;
Below is a structural overview of a BesugoComponent extended class.
constructor
constructor(props) {
super(props);
}
Only mandatory if the component expects to be passed any props (i.e. data from attributes in html). Should always take the form shown above.
config
A static object that defines this component within our website.
(optional) tag: a string representing the placeholder tag passed in the HTML layout and which will be replaced by the actual layout of the component
(optional) categories: content categories that this component will preview in /admin
An optional method to fetch and return whatever data is needed to build the component. If in a CMS preview page, it should fetch the data from the methods supplied by the CMS global object; otherwise it would fetch from its props object.
Use this.isPreview() to check whether we're in a CMS preview content.
If there's a need, you can pass more complicated data from hugo templates as children of the placeholder element, or as a JSON text inside it. The placeholder DOM object is given to the component as this.props.xplaceholder, so you can pass the data however you want as long as you fetch it appropriately.
Example hugo template:
<BlogPost
title="{{ .Title }}"
content="{{ htmlEscape .Content }}"
>
{{ range .Params.people }}
{{ range where (where $.Site.RegularPages "Section" "people") ".Params.title" .person }}
<BlogPostAuthor
link="{{ .URL }}"
title="{{ .Title }}"
></BlogPostAuthor>
{{ end }}
{{ end }}
</BlogPost>
fetch in the component as:
getData() {
const data = Object.assign({
people: []
}, this.props);
if(this.props.xplaceholder) {
const authors = this.props.xplaceholder.querySelectorAll('BlogPostAuthor');
for(let i = 0; i < authors.length; i++) {
let author = authors[i];
let person = {
link: author.getAttribute('link'),
Title: author.getAttribute('title')
};
data.people.push(person);
}
}
// "Content" comes pre-built with HTML markup already. We need to parse it so that it doesn't show up as simple text
const parsed = new DOMParser().parseFromString(data.content, "text/html");
data.content = ReactHtmlParser(parsed.documentElement.textContent);
return data;
}
renderBlock()
Where the actual layout for this component goes; must return a single JSX object, so if necessary you must encapsulate the whole output in an outer div container or equivalent.
Although it's not necessary, when the component will only be used for direct output of a block and not for any content preview, you can define the layout in the render() method instead as you would in a typical React component.
You can also include other components within another component's layouts; don't forget to pass any data necessary to build it:
import React from 'react';
import BesugoComponent from 'Besugo';
import SocialIcons from 'partials/SocialIcons';
// ...
renderBlock() {
const data = this.getData();
return (
<div className="profile__header-info">
<h1 className="profile__header-info__title">{ data.Title }</h1>
<SocialIcons section="profile" { ...data } />
</div>
);
}
renderPreview()
This is the layout for the preview area of this component. Typically, you will encapsulate the above renderBlock() within another JSX layout:
import React from 'react';
import BesugoComponent from 'Besugo';
import SVGElements from 'partials/SVGElements';
import TopHeader from 'partials/TopHeader';
import EndFooter from 'partials/EndFooter';
// ...
renderPreview() {
return (
<div id="cmsPreview">
<SVGElements/>
<TopHeader/>
{ this.renderBlock() }
<EndFooter/>
</div>
);
}
Closes #27: resolves existing layout redundancy.
NOTE: this was branched out of #26, as of its creation still not merged, because it requires the webpack changes from there.
Overview
Any piece of layout code that can potentially be shared between pages, or even repeated inside the same page, should go in its own
BesugoComponent
in a.jsx
file. This way, we won't have to copy-paste repeating layouts in every file, or even rewrite it for CMS previews.BesugoComponent
A Class extension of
React.Component
, what matters most to us is its ability to parse JSX from any data supplied to it, and that it can reuse other components when rendering itself.All such components should have the following basic structure to be built:
Below is a structural overview of a BesugoComponent extended class.
constructor
Only mandatory if the component expects to be passed any props (i.e. data from attributes in html). Should always take the form shown above.
config
A static object that defines this component within our website.
/admin
Example:
getData()
An optional method to fetch and return whatever data is needed to build the component. If in a CMS preview page, it should fetch the data from the methods supplied by the
CMS
global object; otherwise it would fetch from its props object.Use
this.isPreview()
to check whether we're in a CMS preview content.Example:
In most cases, you would pass any necessary data as attributes of that placeholder, and they will be direct properties of the
this.props
object:resolves automatically to
If there's a need, you can pass more complicated data from hugo templates as children of the placeholder element, or as a JSON text inside it. The placeholder DOM object is given to the component as
this.props.xplaceholder
, so you can pass the data however you want as long as you fetch it appropriately.Example hugo template:
fetch in the component as:
renderBlock()
Where the actual layout for this component goes; must return a single JSX object, so if necessary you must encapsulate the whole output in an outer
div
container or equivalent.React elements have some syntax differences, the most important being:
class
attributes should be defined asclassName
instead;style
attributes expect a JS style object rather than a text string:style={ { background: 'red' } }
Example:
Although it's not necessary, when the component will only be used for direct output of a block and not for any content preview, you can define the layout in the
render()
method instead as you would in a typical React component.You can also include other components within another component's layouts; don't forget to pass any data necessary to build it:
renderPreview()
This is the layout for the preview area of this component. Typically, you will encapsulate the above
renderBlock()
within another JSX layout: