All docschevron-rightDr. UIchevron-rightarrow-leftGuideschevron-rightBatfish helpers

Batfish helpers

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.

Arguments

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.

Set up in batfish.config.js

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' }
      ]
    });
}

Usage

import React from 'react';
import navigation from '@mapbox/batfish/data/navigation';

class PageShell extends React.Component {
  render () {
    return (
      <PageLayout navigation={navigation} />;
    )
  }
}

Output

  • 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.

Sample

{
  "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"
    }
  }
}

Filters

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.

Arguments

  • data, object. Provided by the data selector.

Set up in batfish.config.js

const { buildFilters } = require('@mapbox/dr-ui/helpers/batfish/filters.js');

module.exports = () => {
  return {
    dataSelectors: {
      topics: (data) => buildFilters(data)
    }
  };
};

Usage

import React from 'react';
import filters from '@mapbox/batfish/data/filters.js';

class PageShell extends React.Component {
  render() {
    return (
      <PageLayout filters={filters} />;
    )
  }
}

Output

  • The shape of filters is an object, where the top-level keys are pathnames for examplesIndex pages.
    • Each object has a products array, a unique list of products.
    • Each object has a topics array, a unique list of topics.
    • Each object has a pages array of all sub pages to display. The pages array is sorted in the following order:
      1. The "Getting started" topic(s), if it exists. This will make sure the examples on product pages show getting started examples first.
      2. By level, if it exists. This will make sure that beginner level tutorials will appear first on the tutorials page.
      3. All remaining pages are sorted alphabetically by title.
    • If available, the object may have a levels array, a unique list of levels options.
    • If available, the object may have a languages array, a unique list of language options.
    • If available, the object may have videos: true to show that one or more sub pages have video: true in the frontMatter.

Sample

{
  "/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"
      ]
    }
  ]
}
}

Shape of multi-structured 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.

Example

[
  {
    "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
      }
    ]
  }
]

Shape of appended pages

  • 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

split-pages supports groups of pages that use the split page pattern by generating and collecting headings for the main page to reference.

Arguments

  • data, object. Provided by the data selector.

Set up in batfish.config.js

const {
  buildSplitPages
} = require('@mapbox/dr-ui/helpers/batfish/split-pages.js');

module.exports = () => {
  return {
    dataSelectors: {
      splitPages: (data) => buildSplitPages(data)
    }
  };
};

Usage

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>
    );
  }
}

Output

  • The shape is an object, where the top-level keys are pathnames for top level pages that have split pages.
    • Each object has a headings array. It contains all the headings for the page.

Sample

{
  "/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"
      }
    ]
  }
}

Prepare sitemap

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.

Arguments

  • 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.)

Set up in batfish.config.js

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 })
    }
  };
};

Advanced usage

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/`
      ]
    }
  };
};

Output

  • The shape is an array of paths that should be excluded from the sitemap.

Sample

[
  "/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/"
]

Troubleshooting

If data is not building as you expect:

  1. Temporarily change a dataSelector in the batfish.config.js to dump out your pages data as a test fixture (rename 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 })
},
  1. Run npm start and the build process will write the JSON file to the fixture folder.
  2. In 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).
  3. Make any necessary changes to data/navigation.js and/or data/pages.js until all test cases pass.
Was this page helpful?