In the last post, we set the stage and discussed what we're going to build as well as initialized our project structure. Nothing too fancy. It's time to build an actual component though and it seems like the global page header would be a perfect place to start.

Blog Global Header

There are a couple of things I'm going to skip for now. I'm going to exclude everything from the top row for now. I'm not ready to implement any "Subscribe" features and we have no search page, so we'll exclude that functionality. Let's start in the top row and work our way down.

Be sure to check out public/index.html where I defined all the global page structure and our root container where our app will be rendered.

Title Row

Pretty simple row here. We just need a field for the header text. So lets create a Header component.

sitecore/definitions/components/Header.sitecore.js

import { CommonFieldTypes } from '@sitecore-jss/sitecore-jss-manifest';

export default function(manifest) {
  manifest.addComponent({
    name: 'Header',
    fields: [
      { name: 'title', type: CommonFieldTypes.SingleLineText }
    ]
  });
}

After that we'll need to stub out some component data for the data source of the component.

data/component-content/Header.yml

id: header-content
componentName: Header
fields:
  title: Adam Lamarre

The id is how we'll refer to this component in our route data later. componentName specifies our component template that we defined above. After that it's just defining fields and we're only using the title field so far so we'll just fill that out for now.

Let's write some React to display this component.

src/components/Header/index.js

import React from 'react';
import { Text } from '@sitecore-jss/sitecore-jss-react';

let HeaderTop = ({ title }) => (
    <div className="blog-header py-3">
        <div className="row flex-nowrap justify-content-between align-items-center">
            <div className="col-12 text-center">
                <Text tag="span" className="blog-header-logo text-dark" field={title} />
            </div>
        </div>
    </div>
);

let Header = ({ fields }) => (
    <header>
        <HeaderTop title={fields.title} />
    </header>
);

export default Header;

Now let's wire it all up with some route data.

data/routes/en.yml

id: home-page
placeholders:
  jss-main:
   - id: header-content

At this point, we have our Header component proudly displaying with the title "Adam Lamarre."

With this blog, I envision all of the posts living under the category node in the content tree. To accomplish this, I went ahead and stubbed out a listing page template which we'll use to stub out our content items.

sitecore/definitions/templates/ListingPage-template.sitecore.js

import { CommonFieldTypes } from '@sitecore-jss/sitecore-jss-manifest';

export default function(manifest) {
  manifest.addTemplate({
    name: 'ListingPage-Template',
    fields: [{ name: 'title', type: CommonFieldTypes.SingleLineText }],
  });
}

After we have that in place, let's define all of our content items. I manually created all of these content items. An example content item looks like this:

data/content/Business/en.yml

id: page-business
template: ListingPage-Template

If you'd like to see the rest, take a look here.

I really wanted a TreeList to pick these links so let's go back to our Header component and modify to include a TreeList for navLinks. Our full Header component definition should now look like this.

sitecore/definitions/components/Header.sitecore.js

import { CommonFieldTypes } from '@sitecore-jss/sitecore-jss-manifest';

export default function(manifest) {
  manifest.addComponent({
    name: 'Header',
    fields: [
      { name: 'title', type: CommonFieldTypes.SingleLineText },
      { name: 'navLinks', type: CommonFieldTypes.ContentList }
    ]
  });
}

After we've changed that, we can stub out our selected content items in our component data.

data/component-content/Header.yml

id: header-content
componentName: Header
fields:
  title: Adam Lamarre
  navLinks:
   - id: page-world
   - id: page-us
   - id: page-technology
   - id: page-design
   - id: page-culture
   - id: page-business
   - id: page-politics
   - id: page-opinion
   - id: page-science
   - id: page-health
   - id: page-style
   - id: page-travel

Now that we have that stubbed, it's time to modify our Header React component and display the navigation row.

src/components/Header/index.js

import React from 'react';
import { Text } from '@sitecore-jss/sitecore-jss-react';
import { Link } from 'react-router-dom'

// HeaderTop here... trunated for embed

let HeaderNavigation = ({ navLinks }) => (
    <div className="nav-scroller py-1 mb-2">
        <nav className="nav d-flex justify-content-between">
            {navLinks.value.map((listItem, index) => (
                <Link to={"/" + listItem.name} className="p-2 text-muted" key={index}>
                    {listItem.name}
                </Link>
            ))}
        </nav>
    </div>
);

let Header = ({ fields }) => (
    <header>
        <HeaderTop title={fields.title} />
        <HeaderNavigation navLinks={fields.navLinks} />
    </header>
);

export default Header;

Conclusion

You should now have both the Title and Navigation rows displaying on the page. Please check out the full GitHub tag of this work here. In the next few posts we'll continue working our way down the blog homepage before jumping into implementing routing and multiple page types.

I also wonder how many stock images I can find of maps and globes before I run out.. We'll see.