Dr. UI has two functions that are used in dataSelectors
in batfish.config.js
to help build page metadata and site hierarchy. Each data selector has tests to assert the shape of the data.
These dataset functions often use the page's relative pathname as a unique identifier.
navigation
builds data for the dr-ui navigation components like NavigationAccordion
.
Note that the buildNavigation
function uses named parameters.
siteBasePath
, required. The function requires the siteBasePath
.data
, object. Provided by the data selector.sections
, array. Used with multi-structured sections. See Shape of multi-structured sections.addPages
, array. Pages, usually external to the site, to be appended to navigation. See Shape of appended pages. Not compatible with multi-structured sites when used with buildNavigation
. See Shape of multi-structured sections for more on appending pages to multi-structured sites.const {
buildNavigation
} = require('@mapbox/dr-ui/helpers/batfish/navigation.js');
const siteBasePath = '/help';
module.exports = () => {
return {
siteBasePath: siteBasePath,
dataSelectors: {
navigation: (data) => buildNavigation({ siteBasePath, data })
}
};
};
Multi-structured sites require and additional configuration array:
dataSelectors: {
navigation: (data) =>
buildNavigation({
siteBasePath,
data,
sections: [
{ path: 'maps', title: 'Maps SDK for iOS' },
{ path: 'navigation', title: 'Navigation SDK for iOS' }
]
});
}
import React from 'react';
import navigation from '@mapbox/batfish/data/navigation';
class PageShell extends React.Component {
render () {
return (
<PageLayout navigation={navigation} />;
)
}
}
navTabs
. Provides the top-level navigation for the site and is powered by the navOrder
field in the frontMatter of those pages. Provides the data for NavigationAccordion
.hierarchy
. Provides the parent path for each page.{
"navTabs": [
{
"navOrder": 1,
"title": "Components",
"pages": [],
"id": "components",
"path": "/dr-ui/components/"
},
{
"navOrder": 2,
"title": "Guides",
"pages": [
{
"title": "Batfish helpers",
"description": "Batfish data selector functions to build navigation and topic datasets.",
"path": "/dr-ui/guides/batfish-helpers/",
"order": 2,
"layout": "page"
},
{
"title": "PageLayout component",
"description": "All about building with PageLayout.",
"path": "/dr-ui/guides/page-layout/",
"order": 3,
"layout": "page"
},
{
"title": "Grouped guides",
"description": "Create a group of guides that related to a single topic.",
"path": "/dr-ui/guides/grouped-guides/",
"order": 3,
"group": true,
"layout": "page",
"subPages": [
{
"title": "File structure",
"description": "How to set up your src/pages/ folder to use grouped guides.",
"path": "/dr-ui/guides/grouped-guides/file-structure/",
"groupOrder": 1,
"layout": "page"
},
{
"title": "Frontmatter",
"description": "There are two frontmatter fields needed to make grouped guides work: group and groupOrder.",
"path": "/dr-ui/guides/grouped-guides/frontmatter/",
"groupOrder": 2,
"layout": "page"
},
{
"title": "Resulting UI",
"description": "This section of the catalog site illustrates how grouped guides look and work.",
"path": "/dr-ui/guides/grouped-guides/resulting-ui/",
"groupOrder": 3,
"layout": "page"
}
]
},
{
"title": "Split pages",
"description": "This is the first section",
"path": "/dr-ui/guides/split-pages/",
"order": 3,
"layout": "page",
"splitPages": true
},
{
"title": "Multi-structured sites",
"description": "How to build a site with more than one product.",
"path": "/dr-ui/guides/multi-structured/",
"order": 4,
"layout": "page"
},
{
"title": "Test pages",
"description": "A group of test pages.",
"path": "/dr-ui/guides/test-pages/",
"order": 6,
"group": true,
"layout": "page",
"subPages": [
{
"title": "Accessibility test page",
"description": "Page for testing accessibility of components.",
"path": "/dr-ui/guides/test-pages/a11y/",
"groupOrder": 1,
"layout": "page"
},
{
"title": "Headings",
"description": "Page for testing headings.",
"path": "/dr-ui/guides/test-pages/headings/",
"groupOrder": 2,
"layout": "page"
}
]
},
{
"title": "Accessibility test page",
"description": "Page for testing accessibility of components.",
"path": "/dr-ui/guides/test-pages/a11y/",
"groupOrder": 1,
"layout": "page"
},
{
"title": "File structure",
"description": "How to set up your src/pages/ folder to use grouped guides.",
"path": "/dr-ui/guides/grouped-guides/file-structure/",
"groupOrder": 1,
"layout": "page"
},
{
"title": "Frontmatter",
"description": "There are two frontmatter fields needed to make grouped guides work: group and groupOrder.",
"path": "/dr-ui/guides/grouped-guides/frontmatter/",
"groupOrder": 2,
"layout": "page"
},
{
"title": "Headings",
"description": "Page for testing headings.",
"path": "/dr-ui/guides/test-pages/headings/",
"groupOrder": 2,
"layout": "page"
},
{
"title": "Resulting UI",
"description": "This section of the catalog site illustrates how grouped guides look and work.",
"path": "/dr-ui/guides/grouped-guides/resulting-ui/",
"groupOrder": 3,
"layout": "page"
}
],
"id": "guides",
"path": "/dr-ui/guides/"
},
{
"navOrder": 3,
"title": "Examples",
"pages": [],
"id": "examples",
"path": "/dr-ui/examples/"
},
{
"navOrder": 4,
"title": "Tutorials",
"pages": [],
"id": "tutorials",
"path": "https://docs.mapbox.com/help/tutorials?product=api",
"external": true
},
{
"navOrder": 5,
"title": "Troubleshooting",
"pages": [],
"id": "troubleshooting",
"path": "https://docs.mapbox.com/help/troubleshooting?product=api",
"external": true
}
],
"hierarchy": {
"/dr-ui/components/": {
"title": "Components",
"parent": "/dr-ui/components/"
},
"/dr-ui/guides/": {
"title": "Guides",
"parent": "/dr-ui/guides/"
},
"/dr-ui/guides/batfish-helpers/": {
"title": "Guides",
"parent": "/dr-ui/guides/"
},
"/dr-ui/guides/page-layout/": {
"title": "Guides",
"parent": "/dr-ui/guides/"
},
"/dr-ui/guides/grouped-guides/": {
"title": "Guides",
"parent": "/dr-ui/guides/"
},
"/dr-ui/guides/split-pages/": {
"title": "Guides",
"parent": "/dr-ui/guides/"
},
"/dr-ui/guides/multi-structured/": {
"title": "Guides",
"parent": "/dr-ui/guides/"
},
"/dr-ui/guides/grouped-guides/file-structure/": {
"title": "Grouped guides",
"parent": "/dr-ui/guides/grouped-guides/"
},
"/dr-ui/guides/grouped-guides/frontmatter/": {
"title": "Grouped guides",
"parent": "/dr-ui/guides/grouped-guides/"
},
"/dr-ui/guides/test-pages/a11y/": {
"title": "Test pages",
"parent": "/dr-ui/guides/test-pages/"
},
"/dr-ui/guides/grouped-guides/resulting-ui/": {
"title": "Grouped guides",
"parent": "/dr-ui/guides/grouped-guides/"
},
"/dr-ui/guides/test-pages/headings/": {
"title": "Test pages",
"parent": "/dr-ui/guides/test-pages/"
},
"/dr-ui/guides/test-pages/": {
"title": "Guides",
"parent": "/dr-ui/guides/"
},
"/dr-ui/examples/example-1/": {
"title": "Examples",
"parent": "/dr-ui/examples/"
},
"/dr-ui/examples/example-4/": {
"title": "Examples",
"parent": "/dr-ui/examples/"
},
"/dr-ui/examples/example-2/": {
"title": "Examples",
"parent": "/dr-ui/examples/"
},
"/dr-ui/examples/example-5/": {
"title": "Examples",
"parent": "/dr-ui/examples/"
},
"/dr-ui/examples/example-3/": {
"title": "Examples",
"parent": "/dr-ui/examples/"
},
"/dr-ui/examples/": {
"title": "Examples",
"parent": "/dr-ui/examples/"
},
"https://docs.mapbox.com/help/tutorials?product=api": {
"title": "Tutorials",
"parent": "https://docs.mapbox.com/help/tutorials?product=api"
},
"https://docs.mapbox.com/help/troubleshooting?product=api": {
"title": "Troubleshooting",
"parent": "https://docs.mapbox.com/help/troubleshooting?product=api"
}
}
}
Given a group of example
pages, the filters
Batfish helper creates unique option lists for products
, topics
, levels
, and languages
required to power the filter feature for exampleIndex
layout pages. It will also create an array of videos
if any pages have video: true
in the frontMatter.
data
, object. Provided by the data selector.const { buildFilters } = require('@mapbox/dr-ui/helpers/batfish/filters.js');
module.exports = () => {
return {
dataSelectors: {
topics: (data) => buildFilters(data)
}
};
};
import React from 'react';
import filters from '@mapbox/batfish/data/filters.js';
class PageShell extends React.Component {
render() {
return (
<PageLayout filters={filters} />;
)
}
}
examplesIndex
pages.products
array, a unique list of products.topics
array, a unique list of topics.pages
array of all sub pages to display. The pages array is sorted in the following order:level
, if it exists. This will make sure that beginner level tutorials will appear first on the tutorials page.levels
array, a unique list of levels options.languages
array, a unique list of language options.videos: true
to show that one or more sub pages have video: true
in the frontMatter.{
"/dr-ui/examples/" : {
"topics": [
"Getting started",
"Geocoding",
"Navigation"
],
"levels": [
1,
2,
3
],
"languages": [
"JavaScript",
"Kotlin",
"Swift"
],
"videos": true,
"products": [
"Documentation",
"Mapbox Tiling Service"
],
"pages": [
{
"path": "/dr-ui/examples/example-4/",
"title": "Quickstart",
"description": "Get started with this example.",
"contentType": "example",
"language": [
"JavaScript",
"Kotlin"
],
"topics": [
"Getting started"
],
"thumbnail": "header-image",
"layout": "example",
"products": [
"Documentation",
"Mapbox Tiling Service"
]
},
{
"path": "/dr-ui/examples/example-1/",
"title": "Example 1",
"description": "This is example 1.",
"contentType": "example",
"language": [
"JavaScript",
"Kotlin"
],
"topics": [
"Navigation"
],
"level": 1,
"thumbnail": "header-image",
"layout": "example",
"video": true,
"products": [
"Documentation",
"Mapbox Tiling Service"
]
},
{
"path": "/dr-ui/examples/example-2/",
"title": "Example 2",
"description": "This is example 2.",
"contentType": "example",
"language": [
"JavaScript"
],
"topics": [
"Navigation"
],
"level": 2,
"thumbnail": "header-image",
"layout": "example",
"products": [
"Documentation"
]
},
{
"path": "/dr-ui/examples/example-3/",
"title": "Example 3",
"description": "This is example 3.",
"contentType": "example",
"language": [
"Swift"
],
"topics": [
"Navigation",
"Geocoding"
],
"level": 3,
"thumbnail": "header-image",
"layout": "example",
"products": [
"Documentation"
]
},
{
"path": "/dr-ui/examples/example-5/",
"title": "Another example",
"description": "This is another example.",
"contentType": "example",
"language": [
"JavaScript"
],
"topics": [
"Navigation"
],
"thumbnail": "header-image",
"layout": "example",
"products": [
"Documentation",
"Mapbox Tiling Service"
]
}
]
}
}
sections
path
(required) string. Top-level folder in src/pages
.title
(required) string. Title of the product.tag
(optional) string. Name of tag to add to ProductMenu
.addPages
, array. Pages, usually external to the site, to be appended to navigation. See Shape of appended pages.[
{
"path": "maps",
"title": "Maps SDK for iOS",
},
{
"path": "navigation",
"title": "Navigation SDK for iOS"
},
{
"path": "beta/navigation",
"title": "Beta Navigation SDK for iOS",
"tag": "beta"
},
{
"path": "vision",
"title": "Vision SDK for iOS",
"addPages": [
{
"title": "Tutorial",
"path": "https://docs.mapbox.com/help/tutorials?product=vision",
"navOrder": 4
},
{
"title": "Troubleshooting",
"path": "https://docs.mapbox.com/help/troubleshooting?product=vision",
"navOrder": 5
}
]
}
]
title
(required) string. Title of the page to be appended. Example: "Tutorials".path
(required) string. Path of the page to be appended. Example: "https://docs.mapbox.com/help/tutorials?product=navigation".navOrder
(options) number. An integer denoting where the page to be appended should be placed in the order of items in the navigation accordion.split-pages
supports groups of pages that use the split page pattern by generating and collecting headings
for the main page to reference.
data
, object. Provided by the data selector.const {
buildSplitPages
} = require('@mapbox/dr-ui/helpers/batfish/split-pages.js');
module.exports = () => {
return {
dataSelectors: {
splitPages: (data) => buildSplitPages(data)
}
};
};
import React from 'react';
import splitPages from '@mapbox/batfish/data/split-pages';
class PageShell extends React.Component {
render() {
const { children, location, frontMatter } = this.props;
const isSplitPage = splitPages[location.pathname];
return (
<PageLayout headings={isSplitPage ? isSplitPage.headings : undefined}>
{children}
</PageLayout>
);
}
}
headings
array. It contains all the headings for the page.{
"/dr-ui/guides/split-pages/": {
"filePath": "/Users/andrewsepic/mapbox-repos/dr-ui/docs/src/pages/guides/split-pages/index.js",
"path": "/dr-ui/guides/split-pages/",
"frontMatter": {
"title": "Split pages",
"description": "This is the first section",
"splitPages": true,
"order": 3,
"layout": "page",
"contentType": "API"
},
"pages": [
{
"filePath": "/Users/andrewsepic/mapbox-repos/dr-ui/docs/src/pages/guides/split-pages/sections/intro.md",
"path": "/dr-ui/guides/split-pages/sections/intro/",
"frontMatter": {
"title": "Introduction to split pages",
"description": "An introduction to use split pages.",
"order": 1,
"splitPage": true,
"hideFeedback": true,
"products": [
"Documentation"
],
"contentType": "guide"
},
"headings": [],
"slug": "introduction-to-split-pages",
"order": 1
},
{
"filePath": "/Users/andrewsepic/mapbox-repos/dr-ui/docs/src/pages/guides/split-pages/sections/how-to.md",
"path": "/dr-ui/guides/split-pages/sections/how-to/",
"frontMatter": {
"title": "How to use spit pages",
"description": "An introduction to use split pages.",
"order": 2,
"splitPage": true,
"hideFeedback": true,
"contentType": "guide",
"products": [
"Documentation"
],
"prependJs": [
"import MainPage from '!raw-loader!../index.js'; //eslint-disable-line",
"import SubOne from '!raw-loader!./intro.md'; //eslint-disable-line",
"import SplitPageShell from '!raw-loader!../../../../components/split-page-shell.js'; //eslint-disable-line",
"import CodeSnippet from '../../../../../../src/components/code-snippet/code-snippet';",
"import { highlightJsx } from '../../../../../../src/components/highlight/jsx';",
"import { highlightHtml } from '../../../../../../src/components/highlight/html';"
]
},
"headings": [
{
"level": 2,
"text": "How to use split pages",
"slug": "how-to-use-split-pages"
},
{
"level": 3,
"text": "1. Create the main page",
"slug": "1-create-the-main-page"
},
{
"level": 3,
"text": "2. Create the split pages",
"slug": "2-create-the-split-pages"
},
{
"level": 3,
"text": "3. Update the Batfish configuration",
"slug": "3-update-the-batfish-configuration"
},
{
"level": 3,
"text": "4. Create redirects",
"slug": "4-create-redirects"
}
],
"slug": "how-to-use-spit-pages",
"order": 2
},
{
"filePath": "/Users/andrewsepic/mapbox-repos/dr-ui/docs/src/pages/guides/split-pages/sections/limitations.md",
"path": "/dr-ui/guides/split-pages/sections/limitations/",
"frontMatter": {
"title": "Limitations",
"description": "Limitations to the split page pattern.",
"order": 3,
"splitPage": true,
"hideFeedback": true,
"products": [
"Documentation"
],
"contentType": "guide"
},
"headings": [
{
"level": 2,
"text": "Limitations",
"slug": "limitations"
}
],
"slug": "limitations",
"order": 3
},
{
"filePath": "/Users/andrewsepic/mapbox-repos/dr-ui/docs/src/pages/guides/split-pages/sections/tag-debugger.md",
"path": "/dr-ui/guides/split-pages/sections/tag-debugger/",
"frontMatter": {
"title": "Tag debugger",
"description": "Limitations to the split page pattern.",
"order": 4,
"splitPage": true,
"tag": "beta",
"hideFeedback": true,
"products": [
"Documentation"
],
"contentType": "guide"
},
"headings": [
{
"level": 2,
"text": "Tag debugger",
"slug": "tag-debugger",
"tag": "beta"
}
],
"slug": "tag-debugger",
"order": 4
}
],
"headings": [
{
"level": 2,
"text": "How to use split pages",
"slug": "how-to-use-split-pages"
},
{
"level": 3,
"text": "1. Create the main page",
"slug": "1-create-the-main-page"
},
{
"level": 3,
"text": "2. Create the split pages",
"slug": "2-create-the-split-pages"
},
{
"level": 3,
"text": "3. Update the Batfish configuration",
"slug": "3-update-the-batfish-configuration"
},
{
"level": 3,
"text": "4. Create redirects",
"slug": "4-create-redirects"
},
{
"level": 2,
"text": "Limitations",
"slug": "limitations"
},
{
"level": 2,
"text": "Tag debugger",
"slug": "tag-debugger",
"tag": "beta"
}
]
}
}
prepareSitemap
creates a data file that lists all files with hideFromSearchEngines: true
or splitPage: true
set in the page's frontMatter. Use this generated file with Batfish's sitemap.ignoreFile
option to generate a sitemap that excludes any paths in the prepared file.
data
, object. Provided by the data selector.siteBasePath
, string. The site's siteBasePath
as defined in the Batfish config.outputDirectory
, string. Batfish's outputDirectory
. Default _site
.docsPath
, string. The directory that batfish.config.js
lives in, if not in the root. (It's unlikely that you will use this option.)const { prepareSitemap } = require('@mapbox/dr-ui/helpers/batfish/index.js');
module.exports = () => {
return {
sitemap: {
// generated by the sitemapIgnore dataSelector
ignoreFile: '_site_tmp/data/sitemap-ignore.js'
},
dataSelectors: {
sitemapIgnore: ({ pages }) => prepareSitemap({ pages, siteBasePath })
}
};
};
For sites that generate demos, like mapbox-gl-js and mts-docs, you can exclude the assets folder:
const { prepareSitemap } = require('@mapbox/dr-ui/helpers/batfish/index.js');
module.exports = () => {
return {
sitemap: {
// generated by the sitemapIgnore dataSelector
ignoreFile: '_site_tmp/data/sitemap-ignore.js'
},
dataSelectors: {
sitemapIgnore: ({ pages }) => [
...prepareSitemap({ pages, siteBasePath }),
// exclude demos generated by webpack loader
`${process.cwd()}/_site/assets/`
]
}
};
};
[
"/Users/andrewsepic/mapbox-repos/dr-ui/docs/_site/index.html",
"/Users/andrewsepic/mapbox-repos/dr-ui/docs/_site/guides/split-pages/sections/how-to/",
"/Users/andrewsepic/mapbox-repos/dr-ui/docs/_site/guides/split-pages/sections/limitations/",
"/Users/andrewsepic/mapbox-repos/dr-ui/docs/_site/guides/split-pages/sections/tag-debugger/",
"/Users/andrewsepic/mapbox-repos/dr-ui/docs/_site/guides/split-pages/sections/intro/"
]
If data is not building as you expect:
data-example.json
to something meaningful to your use case):navigation: data => {
require('fs').writeFileSync('./tests/fixtures/data-example.json', JSON.stringify(data, null, 2))
return buildNavigation({ siteBasePath, data })
},
npm start
and the build process will write the JSON file to the fixture folder.tests/navigation.test.js
and/or tests/filters.test.js
create a new test case that loads in data-example.json
(rather than data.json).data/navigation.js
and/or data/pages.js
until all test cases pass.