diff --git a/CHANGELOG.md b/CHANGELOG.md
index d8241c772..2f9467bbe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,22 @@
+# [1.40.0](https://github.com/newrelic/developer-website/compare/v1.39.0...v1.40.0) (2021-04-02)
+
+
+### Bug Fixes
+
+* **logo:** remove rogue semicolons so fill colors render ([8fca904](https://github.com/newrelic/developer-website/commit/8fca9042375123d847220c16431aac27a982090e))
+
+
+### Features
+
+* **logo:** update logo in side nav and footer ([8e6e205](https://github.com/newrelic/developer-website/commit/8e6e205765d9d2a7e6fe7d71a71e620fea309a1c))
+
+# [1.39.0](https://github.com/newrelic/developer-website/compare/v1.38.0...v1.39.0) (2021-03-29)
+
+
+### Features
+
+* **embeds:** create embed pages ([e84f1e8](https://github.com/newrelic/developer-website/commit/e84f1e895a3c1d3240845943ceb54fcc61e7245f))
+
# [1.38.0](https://github.com/newrelic/developer-website/compare/v1.37.0...v1.38.0) (2021-03-24)
diff --git a/COMPONENT_GUIDE.md b/COMPONENT_GUIDE.md
index 74b46acda..6a6969fd0 100644
--- a/COMPONENT_GUIDE.md
+++ b/COMPONENT_GUIDE.md
@@ -168,95 +168,191 @@ A step description
> Note: keep in mind that a new line is necessary after an `img` tag to ensure proper rendering of subsequent text/markdown.
-# Tutorial
+# Tutorial
## Usage
-The `` component can be used to help a user step through changes in code by highlighting the difference between each step. It utilizes children `` and `` components to find codeblocks with new code based off of the starting code. You can set the starting code as the first child of the `` with the `` component. In order to use the tutorial component, you must set a `fileName` for your codeblocks so that the parser can find the corresponding codeblocks with changes in them.
+You can use the `Tutorial` component walk a user through changes in code by automatically highlighting the difference between each step.
+
+### Define your starting codebase
+
+First, use a `Project` component to define your starting codebase:
````md
-
+
+
+
+
+```jsx fileName=first-file.js
+const myCode = "here is my first file"
+```
-
+```jsx fileName=second-file.js
+const myCode = "here is my second file"
+```
- ```jsx fileName="myfile.js"
- const myCode = "here is my starting code"
- ```
+
-
+
+````
- ## Here is my tutorial!
+In order to use the tutorial component, you must set a `fileName` for your codeblocks so that the parser can find the corresponding codeblocks with changes in them.
-
+Note that you can set up multiple code files in your `Project` component. The parser will track changes in each of the codeblocks throughout the `Tutorial`. Each codeblock is presented in its own tab, which mimics how a user might actually edit these files in their IDE.
-
+### Update code in tutorial steps
- ```jsx fileName="myfile.js"
- const myCode = "here is my code"
- const myNewCode = "here is my new code"
- ```
+Use `Steps` to show changes to your starting files:
-
+````md
+
-
+
- ```jsx fileName="myfile.js"
- const myCode = "here is my code"
- const myNewCode = "here is my new code"
- const evenMoreNewCode = "he is even more new code"
- ```
+```jsx fileName=first-file.js
+const myCode = "here is my first file"
+```
-
+```jsx fileName=second-file.js
+const myCode = "here is my second file"
+```
+
+
+
+## Here is my tutorial!
+
+
+
+
+
+Update your first file:
+
+```jsx fileName=first-file.js
+const myCode = "here is my first file"
+const myNewCode = "here is my new code"
+```
+
+
+
+
+
+Update your second file:
+
+```jsx fileName=second-file.js
+const myCode = "here is my second file"
+const myNewCode = "here is my new code"
+```
+
+
+
+
+
+Update your first file again:
+
+```jsx fileName=first-file.js
+const myCode = "here is my first file"
+const myNewCode = "here is my new code"
+const evenMoreNewCode = "here is even more new code"
+```
+
+
+
+
-
+
````
-In this example, for the first step the second line (`myNewCode`) will be highlighted, and for the second step, the third line (`evenMoreNewCode`) will be highlighted.
+In the first step's rendered codeblock, the second line (`myNewCode`) in _first-file.js_ will be highlighted. In the second step, the second line (`myNewCode`) in second-file.js_ will be highlighted. In the third step, the third line (`evenMoreNewCode`) iin _first-file.js_ will be highlighted.
-You can also pass in multiple codeblocks to the `` component and the parser will track all changes in each of the codeblocks. The multiple codeblocks will show up as tabs of the same editor, mimicking how a user might actually be editing these files in their IDE. For example:
+Even though a single file is highlighted in each step, all files are rendered in tabs for each step on the page. Your reader can then toggle between the files to see what the current state of the whole codebase is.
+
+## Things to keep in mind
+
+Here are some things to keep in mind when using the `Tutorial` component in your developer guides.
+
+### Only change code in one file per step
+
+While every file is shown in a tabulated codeblock in every step, you can only change code in one file per step. This is because when you include a file change in a `Tutorial` step, the file that was changed is presented first in the tabulated codeblock.
+
+`Tutorial` doesn't know how to render a tabulated codeblock when multiple files are changed in the same step:
````md
-
+
+
+
+
+```jsx fileName=first-file.js
+const myCode = "here is my first file"
+```
+
+```jsx fileName=second-file.js
+const myCode = "here is my second file"
+```
+
+
+
+## Here is my tutorial!
+
+
+
+
+
+Update your first file and second file in the same step:
+
+```jsx fileName=first-file.js
+const myCode = "here is my first file"
+const myNewCode = "here is my new code"
+```
+
+```jsx fileName=second-file.js
+const myCode = "here is my second file"
+const myNewCode = "here is my new code"
+```
+
+
+
+
+
+
+````
-
+This won't render properly in your guide.
- ```jsx fileName="myfile.js"
- const myCode = "here is my starting code"
- ```
+### `Tutorial` doesn't show subtractive changes
- ```css fileName="mystyles.css"
- .myStyle {
- color: blue;
- }
- ```
+Some code diff tools show additive changes (you created a new line) and subtractive changes (you deleted a line). `Tutorial` doesn't call to attention any code deletions.
-
+````md
+
- ## Here is my tutorial!
+
-
+```jsx fileName=first-file.js
+const myCode = "here is my first file"
+const moreCode = "here is more code"
+const evenMoreCode = "here is even more code"
+```
-
+
- ```jsx fileName="myfile.js"
- const myCode = "here is my code"
- const myNewCode = "here is my new code"
- ```
+## Here is my tutorial!
-
+
-
+
- ```css fileName="mystyles.css"
- .myStyle {
- color: blue;
- font-size: 1000px;
- }
- ```
+Delete `moreCode`:
-
+```jsx fileName=first-file.js
+const myCode = "here is my first file"
+const evenMoreCode = "here is even more code"
+```
+
+
+
+
-
+
````
-In this example, in the first step, line 2 will be highlighted (`myNewCode`) and in the second step, line 3 will be highlighted (`font-size: 1000px`). Both steps will have both files, where as the default file shown will be `myfile.js` for the first step and `mystyles.css` for the second step. Users can toggle between the two files to see what the current state of the code is.
\ No newline at end of file
+In this case, `Tutorial` will show _first-file.js_, but it won't indicate that the second constant (`moreCode`) was removed.
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f20c5edbc..b861bcc60 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -83,7 +83,7 @@ you can use the Github `Edit This File` button to submit a change.
### Cloning vs Forking
-To be able to [Clone](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) this repository and contribute you will need to be given write access to the repository. This is reserved for New Relic Employees only. Contact the Developer Experience team (developer-website-content Slack channel) if you need write access.
+To be able to [Clone](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) this repository and contribute you will need to be given write access to the repository. This is reserved for New Relic Employees only. Contact the Developer Experience team (#help-deven-websites Slack channel) if you need write access.
As a non New Relic employee you can [Fork](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) the repository and contribute as needed.
diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md
index 544855297..42be073cd 100644
--- a/STYLE_GUIDE.md
+++ b/STYLE_GUIDE.md
@@ -39,6 +39,7 @@
- [GuideTemplate Frontmatter slugs](#guidetemplate-frontmatter-slugs)
- [GuideTemplate Frontmatter example](#guidetemplate-frontmatter-example)
- [Reusable components](#reusable-components)
+ - [Embed guides](#embed-guides)
- [Grammar and formatting](#grammar-and-formatting)
- [Format titles](#format-titles)
- [Format headers](#format-headers)
@@ -339,6 +340,22 @@ tags:
In order to drive simplicity and ease of use New Relic has provided a set of reusable components you can leverage
when creating documentation. Refer to our [Component Guide](COMPONENT_GUIDE.md) for more information.
+## Embed guides
+
+Each guide on the site (in frontmatter `template: GuideTemplate`) has an embed page automatically generated. The page URL has the same path as the guide with `/embed` appended to the end. The embed page contains only the body content of the `.mdx` file and no other site components (nav, header, footer, etc). The page defaults to light mode.
+
+### Example
+
+If there's a guide with this URL:
+
+ https://developer.newrelic.com/path/to/guide
+
+The embed page URL would be:
+
+ https://developer.newrelic.com/path/to/guide/embed
+
+You can use the embed URL in an `` on another site to display guide content.
+
## Grammar and formatting
### Format titles
diff --git a/gatsby-browser.js b/gatsby-browser.js
index 54dca922a..1c62c4d2f 100644
--- a/gatsby-browser.js
+++ b/gatsby-browser.js
@@ -49,24 +49,4 @@ const onInitialClientRender = () => {
}
};
-const onRouteUpdate = ({ location }) => {
- if (process.env.NODE_ENV !== `production` || typeof ga !== `function`) {
- return null;
- }
-
- // wrap inside a timeout to make sure react-helmet is done with it's changes (https://github.com/gatsbyjs/gatsby/issues/9139)
- // reactHelmet is using requestAnimationFrame: https://github.com/nfl/react-helmet/blob/5.2.0/src/HelmetUtils.js#L296-L299
- const sendPageView = () => {
- const pagePath = location
- ? location.pathname + location.search + location.hash
- : undefined;
- window.ga(`set`, `page`, pagePath);
- window.ga(`send`, `pageview`);
- };
-
- // Minimum delay for reactHelmet's requestAnimationFrame
- setTimeout(sendPageView, 32);
-
- return null;
-};
-export { onRouteUpdate, wrapPageElement, onInitialClientRender };
+export { wrapPageElement, onInitialClientRender };
diff --git a/gatsby-config.js b/gatsby-config.js
index 13322bfc2..c09d617ec 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -16,19 +16,12 @@ module.exports = {
{
resolve: '@newrelic/gatsby-theme-newrelic',
options: {
- gaTrackingId: 'UA-3047412-33',
layout: {
contentPadding: '2rem',
maxWidth: '1700px',
component: require.resolve('./src/layouts'),
mobileBreakpoint: '760px',
},
- // workaround until this is no longer needed.
- // https://github.com/newrelic/gatsby-theme-newrelic/issues/302
- i18n: {
- translationsPath: `${__dirname}/src/i18n/translations`,
- additionalLocales: [],
- },
prism: {
languages: ['yaml', 'sass', 'scss', 'java'],
},
@@ -102,6 +95,20 @@ module.exports = {
},
},
},
+ tessen: {
+ product: 'DEV',
+ subproduct: 'TDEV',
+ segmentWriteKey: 'Ako0hclX8WGHwl9rm4n5uxLtT4wgEtuU',
+ trackPageViews: true,
+ pageView: {
+ name: 'pageView',
+ category: 'DocPageView',
+ getProperties: ({ location, env }) => ({
+ path: location.pathname,
+ env: env === 'production' ? 'prod' : env,
+ }),
+ },
+ },
},
},
'gatsby-plugin-sass',
@@ -155,6 +162,7 @@ module.exports = {
release: 'release-2046',
},
},
+ 'gatsby-plugin-embed-pages',
'gatsby-plugin-meta-redirect',
{
resolve: 'gatsby-plugin-gdpr-tracking',
diff --git a/package.json b/package.json
index a3af0eac8..7da4677ae 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "developer-website",
"private": true,
- "version": "1.38.0",
+ "version": "1.40.0",
"dependencies": {
"@emotion/core": "^10.1.1",
"@emotion/styled": "^10.0.27",
diff --git a/plugins/gatsby-plugin-embed-pages/gatsby-node.js b/plugins/gatsby-plugin-embed-pages/gatsby-node.js
new file mode 100644
index 000000000..3de2fe741
--- /dev/null
+++ b/plugins/gatsby-plugin-embed-pages/gatsby-node.js
@@ -0,0 +1,45 @@
+const path = require('path');
+
+exports.createPages = async ({ actions, graphql, reporter }) => {
+ const { createPage } = actions;
+
+ const { data } = await graphql(`
+ query MyQuery {
+ allMdx(filter: { frontmatter: { template: { eq: "GuideTemplate" } } }) {
+ nodes {
+ fields {
+ fileRelativePath
+ slug
+ }
+ frontmatter {
+ title
+ path
+ }
+ body
+ }
+ }
+ }
+ `);
+
+ data.allMdx.nodes.forEach((node) => {
+ const {
+ frontmatter,
+ fields: { fileRelativePath, slug },
+ body,
+ } = node;
+ const nodePath = frontmatter.path || slug;
+ const pagePath = path.join(nodePath, 'embed');
+ const contentSourcePath = nodePath;
+
+ createPage({
+ path: pagePath,
+ component: path.resolve(`src/templates/embedPage.js`),
+ context: {
+ slug,
+ fileRelativePath,
+ contentSourcePath,
+ layout: 'EmbedLayout',
+ },
+ });
+ });
+};
diff --git a/plugins/gatsby-source-swiftype/package.json b/plugins/gatsby-plugin-embed-pages/package.json
similarity index 100%
rename from plugins/gatsby-source-swiftype/package.json
rename to plugins/gatsby-plugin-embed-pages/package.json
diff --git a/plugins/gatsby-source-swiftype/gatsby-node.js b/plugins/gatsby-source-swiftype/gatsby-node.js
deleted file mode 100644
index 4590b7bd6..000000000
--- a/plugins/gatsby-source-swiftype/gatsby-node.js
+++ /dev/null
@@ -1,93 +0,0 @@
-const fs = require('fs');
-const createRelatedResourceNode = require('./src/createRelatedResourceNode');
-const getRelatedResources = require('./src/getRelatedResources');
-
-const writeableData = {};
-
-exports.onPreBootstrap = (_, pluginOptions) => {
- const { file } = pluginOptions;
-
- if (!fs.existsSync(file)) {
- fs.writeFileSync(file, '{}');
- }
-};
-
-exports.onCreateNode = async (
- { actions, node, getNodesByType, createNodeId, createContentDigest },
- pluginOptions
-) => {
- const { createNode, createParentChildLink } = actions;
- const { filterNode = () => false, getPath } = pluginOptions;
-
- if (node.internal.type !== 'Mdx' || !filterNode({ node })) {
- return;
- }
-
- const [
- {
- siteMetadata: { siteUrl },
- },
- ] = getNodesByType('Site');
-
- const pathname = getPath({ node });
- const resources = await getRelatedResources({ node, siteUrl }, pluginOptions);
-
- writeableData[pathname] = resources;
-
- resources.forEach((resource) => {
- const child = createRelatedResourceNode({
- parent: node.id,
- resource,
- createContentDigest,
- createNode,
- createNodeId,
- });
-
- createParentChildLink({ parent: node, child: child });
- });
-};
-
-exports.createSchemaCustomization = ({ actions }) => {
- const { createTypes } = actions;
-
- const typeDefs = `
- type RelatedResource implements Node {
- id: ID!
- title: String!
- url: String!
- }
- `;
-
- createTypes(typeDefs);
-};
-
-exports.createResolvers = ({ createResolvers }) => {
- createResolvers({
- Mdx: {
- relatedResources: {
- args: {
- limit: {
- type: 'Int',
- defaultValue: 5,
- },
- },
- type: ['RelatedResource!'],
- resolve: (source, args, context) => {
- const { limit } = args;
-
- return context.nodeModel
- .getNodesByIds({ ids: source.children })
- .slice(0, Math.max(limit, 0));
- },
- },
- },
- });
-};
-
-exports.onPostBootstrap = (_, pluginOptions) => {
- const { refetch, file } = pluginOptions;
-
- if (refetch) {
- fs.writeFileSync(file, JSON.stringify(writeableData, null, 2));
- }
-};
diff --git a/plugins/gatsby-source-swiftype/src/createRelatedResourceNode.js b/plugins/gatsby-source-swiftype/src/createRelatedResourceNode.js
deleted file mode 100644
index 249c4c238..000000000
--- a/plugins/gatsby-source-swiftype/src/createRelatedResourceNode.js
+++ /dev/null
@@ -1,25 +0,0 @@
-module.exports = ({
- createNode,
- createNodeId,
- createContentDigest,
- resource,
- parent,
-}) => {
- const node = {
- id: createNodeId(`RelatedResource-${resource.url}`),
- title: resource.title,
- url: resource.url,
- parent,
- children: [],
- plugin: 'gatsby-source-swiftype',
- internal: {
- type: 'RelatedResource',
- content: JSON.stringify(resource),
- contentDigest: createContentDigest(resource),
- },
- };
-
- createNode(node);
-
- return node;
-};
diff --git a/plugins/gatsby-source-swiftype/src/getRelatedResources.js b/plugins/gatsby-source-swiftype/src/getRelatedResources.js
deleted file mode 100644
index 40a4fa8c5..000000000
--- a/plugins/gatsby-source-swiftype/src/getRelatedResources.js
+++ /dev/null
@@ -1,26 +0,0 @@
-const fs = require('fs');
-const search = require('./search');
-
-module.exports = async ({ node, siteUrl }, pluginOptions) => {
- const {
- refetch,
- engineKey,
- limit,
- file,
- getParams = () => ({}),
- getPath,
- } = pluginOptions;
-
- const pathname = getPath({ node });
-
- if (refetch) {
- return search(siteUrl + pathname, getParams({ node }), {
- engineKey,
- limit,
- });
- }
-
- const data = JSON.parse(fs.readFileSync(file));
-
- return data[pathname] || [];
-};
diff --git a/plugins/gatsby-source-swiftype/src/search.js b/plugins/gatsby-source-swiftype/src/search.js
deleted file mode 100644
index b67985e32..000000000
--- a/plugins/gatsby-source-swiftype/src/search.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const fetch = require('node-fetch');
-const { appendTrailingSlash, stripTrailingSlash } = require('./utils/url');
-
-const normalizeUrl = (url) => {
- const prefix = url.startsWith('!') ? '!' : '';
- const plainUrl = url.replace(/^!/, '');
-
- return [
- prefix + appendTrailingSlash(plainUrl),
- prefix + stripTrailingSlash(plainUrl),
- ];
-};
-
-const uniq = (arr) => [...new Set(arr)];
-
-module.exports = async (url, params = {}, { engineKey, limit }) => {
- const { page: pageFilters = {} } = params.filters || {};
-
- const res = await fetch(
- 'https://search-api.swiftype.com/api/v1/public/engines/search.json',
- {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- ...params,
- engine_key: engineKey,
- per_page: limit,
- filters: {
- ...params.filters,
- page: {
- ...pageFilters,
- url: uniq([
- ...normalizeUrl(`!${url}`),
- ...(pageFilters.url || []).flatMap(normalizeUrl),
- ]),
- },
- },
- }),
- }
- );
-
- const { records } = await res.json();
-
- return records.page;
-};
diff --git a/plugins/gatsby-source-swiftype/src/utils/url.js b/plugins/gatsby-source-swiftype/src/utils/url.js
deleted file mode 100644
index b6eb1a7e1..000000000
--- a/plugins/gatsby-source-swiftype/src/utils/url.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const hasQueryParams = (urlString) => {
- const url = new URL(urlString);
-
- return Boolean(url.search);
-};
-
-exports.appendTrailingSlash = (url) => {
- if (hasQueryParams(url)) {
- return url;
- }
-
- return url.endsWith('/') ? url : `${url}/`;
-};
-
-exports.stripTrailingSlash = (url) => {
- if (hasQueryParams(url)) {
- return url;
- }
-
- return url.endsWith('/') ? url.replace(/\/$/, '') : url;
-};
diff --git a/src/@newrelic/gatsby-theme-newrelic/components/Logo.js b/src/@newrelic/gatsby-theme-newrelic/components/Logo.js
index ad6b09e35..8aa32fb4e 100644
--- a/src/@newrelic/gatsby-theme-newrelic/components/Logo.js
+++ b/src/@newrelic/gatsby-theme-newrelic/components/Logo.js
@@ -2,35 +2,36 @@ import React from 'react';
import PropTypes from 'prop-types';
import { css } from '@emotion/core';
-const bracket = css`
- stroke: var(--color-brand-500);
- stroke-width: 2;
- stroke-linecap: round;
- stroke-linejoin: round;
-
- .dark-mode & {
- stroke: var(--color-brand-300);
- }
-`;
-
const Logo = ({ className, width }) => (
);
diff --git a/src/components/DevSiteSeo.js b/src/components/DevSiteSeo.js
index 791ee8cf8..a82862751 100644
--- a/src/components/DevSiteSeo.js
+++ b/src/components/DevSiteSeo.js
@@ -34,7 +34,7 @@ function DevSiteSeo({ description, meta, title, tags, location }) {
{ property: 'og:title', content: title },
{ property: 'og:description', content: metaDescription },
{ property: 'og:type', content: 'website' },
- { name: 'twitter:card', content: 'summary' },
+ { name: 'twitter:card', content: 'summary_large_image' },
{ name: 'twitter:creator', content: site.siteMetadata.author },
{ name: 'twitter:title', content: title },
{ name: 'twitter:description', content: metaDescription },
diff --git a/src/data/nav.yml b/src/data/nav.yml
index bfce7f5a7..a98c2cd8b 100644
--- a/src/data/nav.yml
+++ b/src/data/nav.yml
@@ -64,8 +64,6 @@
url: '/build-apps/howto-use-nrone-table-components'
- title: Add, query, and mutate data using NerdStorage
url: '/build-apps/add-query-mutate-data-nerdstorage'
- - title: See all open source apps
- url: https://opensource.newrelic.com/nerdpacks/
- title: Build custom visualizations for dashboards
url: '/build-apps/build-visualization'
- title: Custom visualizations and the New Relic One SDK
@@ -74,6 +72,63 @@
url: '/build-apps/customize-visualizations-with-configuration'
- title: Add custom visualizations to your dashboards
url: '/build-apps/add-visualizations-to-dashboard'
+ - title: Build an A/B test application
+ url: '/ab-test'
+ pages:
+ - title: Spin up your demo services
+ url: '/build-apps/ab-test/demo-setup'
+ - title: Install and configure the New Relic One CLI
+ url: '/build-apps/ab-test/install-nr1'
+ - title: Create a Nerdpack
+ url: '/build-apps/ab-test/create-nerdpack'
+ - title: Serve your New Relic One application
+ url: '/build-apps/ab-test/serve-app'
+ - title: Add chart components to your A/B test application
+ url: '/build-apps/ab-test/add-charts'
+ - title: Add your first chart
+ url: '/build-apps/ab-test/first-chart'
+ - title: Add pie charts
+ url: '/build-apps/ab-test/pie-charts'
+ - title: Add tables
+ url: '/build-apps/ab-test/table-charts'
+ - title: Add a chart group
+ url: '/build-apps/ab-test/chart-group'
+ - title: Add user interface components to your application
+ url: '/build-apps/ab-test/add-ui'
+ - title: Add a grid
+ url: '/build-apps/ab-test/grid'
+ - title: Add chart headings
+ url: '/build-apps/ab-test/chart-headings'
+ - title: Add version descriptions
+ url: '/build-apps/ab-test/version-descriptions'
+ - title: Add a section to end your test
+ url: '/build-apps/ab-test/end-test'
+ - title: Persist the selected version
+ url: '/build-apps/ab-test/persist-version'
+ - title: Present an end test confirmation modal
+ url: '/build-apps/ab-test/confirmation-modal'
+ - title: Add NrqlQuery components to your nerdlet
+ url: '/build-apps/ab-test/nrql'
+ - title: Customize NRQL data
+ url: '/build-apps/ab-test/nrql-customizations'
+ - title: Access NerdStorage from your nerdlet
+ url: '/build-apps/ab-test/nerdstorage'
+ - title: Access NerdStorageVault from your nerdlet
+ url: '/build-apps/ab-test/nerdstoragevault'
+ - title: Fetch data from a third-party service
+ url: '/build-apps/ab-test/third-party-service'
+ - title: Add PlatformStateContext to your nerdlet
+ url: '/build-apps/ab-test/platform-state-context'
+ - title: Add navigation to your nerdlet
+ url: '/build-apps/ab-test/navigation'
+ - title: Describe your app for the catalog
+ url: '/build-apps/ab-test/catalog'
+ - title: Publish your New Relic One application
+ url: '/build-apps/ab-test/publish'
+ - title: Subscribe to your New Relic One application
+ url: '/build-apps/ab-test/subscribe'
+ - title: See all open source apps
+ url: https://opensource.newrelic.com/nerdpacks/
- title: Try our APIs
icon: nr-share
url: '/try-our-apis'
diff --git a/src/images/ab-test/ab-test.png b/src/images/ab-test/ab-test.png
new file mode 100644
index 000000000..b2a1b299a
Binary files /dev/null and b/src/images/ab-test/ab-test.png differ
diff --git a/src/images/ab-test/add-details-from-launcher.png b/src/images/ab-test/add-details-from-launcher.png
new file mode 100644
index 000000000..af3479dbb
Binary files /dev/null and b/src/images/ab-test/add-details-from-launcher.png differ
diff --git a/src/images/ab-test/add-this-app.png b/src/images/ab-test/add-this-app.png
new file mode 100644
index 000000000..f07280def
Binary files /dev/null and b/src/images/ab-test/add-this-app.png differ
diff --git a/src/images/ab-test/api-keys.png b/src/images/ab-test/api-keys.png
new file mode 100644
index 000000000..da0a70dab
Binary files /dev/null and b/src/images/ab-test/api-keys.png differ
diff --git a/src/images/ab-test/api-token-prompt.png b/src/images/ab-test/api-token-prompt.png
new file mode 100644
index 000000000..acd51d6e4
Binary files /dev/null and b/src/images/ab-test/api-token-prompt.png differ
diff --git a/src/images/ab-test/app-about.png b/src/images/ab-test/app-about.png
new file mode 100644
index 000000000..ab6e003eb
Binary files /dev/null and b/src/images/ab-test/app-about.png differ
diff --git a/src/images/ab-test/app-in-catalog.png b/src/images/ab-test/app-in-catalog.png
new file mode 100644
index 000000000..2cd9c8a62
Binary files /dev/null and b/src/images/ab-test/app-in-catalog.png differ
diff --git a/src/images/ab-test/app-meta-in-catalog.png b/src/images/ab-test/app-meta-in-catalog.png
new file mode 100644
index 000000000..987d81bbd
Binary files /dev/null and b/src/images/ab-test/app-meta-in-catalog.png differ
diff --git a/src/images/ab-test/chart-group-syncing.png b/src/images/ab-test/chart-group-syncing.png
new file mode 100644
index 000000000..fe52251db
Binary files /dev/null and b/src/images/ab-test/chart-group-syncing.png differ
diff --git a/src/images/ab-test/charts-design-guide.png b/src/images/ab-test/charts-design-guide.png
new file mode 100644
index 000000000..914e3e802
Binary files /dev/null and b/src/images/ab-test/charts-design-guide.png differ
diff --git a/src/images/ab-test/charts-layout.png b/src/images/ab-test/charts-layout.png
new file mode 100644
index 000000000..75ffbf0b7
Binary files /dev/null and b/src/images/ab-test/charts-layout.png differ
diff --git a/src/images/ab-test/copy-existing-key.png b/src/images/ab-test/copy-existing-key.png
new file mode 100644
index 000000000..448f67901
Binary files /dev/null and b/src/images/ab-test/copy-existing-key.png differ
diff --git a/src/images/ab-test/create-key-button.png b/src/images/ab-test/create-key-button.png
new file mode 100644
index 000000000..c0036efd4
Binary files /dev/null and b/src/images/ab-test/create-key-button.png differ
diff --git a/src/images/ab-test/create-key-details.png b/src/images/ab-test/create-key-details.png
new file mode 100644
index 000000000..c2a2995a2
Binary files /dev/null and b/src/images/ab-test/create-key-details.png differ
diff --git a/src/images/ab-test/data-explorer-subscription-count.png b/src/images/ab-test/data-explorer-subscription-count.png
new file mode 100644
index 000000000..677121573
Binary files /dev/null and b/src/images/ab-test/data-explorer-subscription-count.png differ
diff --git a/src/images/ab-test/data-explorer-subscription-dimensions.png b/src/images/ab-test/data-explorer-subscription-dimensions.png
new file mode 100644
index 000000000..c61f248c6
Binary files /dev/null and b/src/images/ab-test/data-explorer-subscription-dimensions.png differ
diff --git a/src/images/ab-test/data-explorer-subscription-edit-nrql.png b/src/images/ab-test/data-explorer-subscription-edit-nrql.png
new file mode 100644
index 000000000..c34c9f029
Binary files /dev/null and b/src/images/ab-test/data-explorer-subscription-edit-nrql.png differ
diff --git a/src/images/ab-test/data-explorer-subscription-page-version.png b/src/images/ab-test/data-explorer-subscription-page-version.png
new file mode 100644
index 000000000..8f78a58b0
Binary files /dev/null and b/src/images/ab-test/data-explorer-subscription-page-version.png differ
diff --git a/src/images/ab-test/data-explorer-subscription.png b/src/images/ab-test/data-explorer-subscription.png
new file mode 100644
index 000000000..d4715397c
Binary files /dev/null and b/src/images/ab-test/data-explorer-subscription.png differ
diff --git a/src/images/ab-test/default-message.png b/src/images/ab-test/default-message.png
new file mode 100644
index 000000000..62d0be4c3
Binary files /dev/null and b/src/images/ab-test/default-message.png differ
diff --git a/src/images/ab-test/demo-service.png b/src/images/ab-test/demo-service.png
new file mode 100644
index 000000000..5a14b37bb
Binary files /dev/null and b/src/images/ab-test/demo-service.png differ
diff --git a/src/images/ab-test/dev-version.png b/src/images/ab-test/dev-version.png
new file mode 100644
index 000000000..19d53cb92
Binary files /dev/null and b/src/images/ab-test/dev-version.png differ
diff --git a/src/images/ab-test/end-test-modal.png b/src/images/ab-test/end-test-modal.png
new file mode 100644
index 000000000..3e563c8ee
Binary files /dev/null and b/src/images/ab-test/end-test-modal.png differ
diff --git a/src/images/ab-test/end-test-section-v1.png b/src/images/ab-test/end-test-section-v1.png
new file mode 100644
index 000000000..5b7cf1d19
Binary files /dev/null and b/src/images/ab-test/end-test-section-v1.png differ
diff --git a/src/images/ab-test/end-test-section-v2.png b/src/images/ab-test/end-test-section-v2.png
new file mode 100644
index 000000000..5e16d6c70
Binary files /dev/null and b/src/images/ab-test/end-test-section-v2.png differ
diff --git a/src/images/ab-test/entity-guid.png b/src/images/ab-test/entity-guid.png
new file mode 100644
index 000000000..42b823b62
Binary files /dev/null and b/src/images/ab-test/entity-guid.png differ
diff --git a/src/images/ab-test/entity-metadata.png b/src/images/ab-test/entity-metadata.png
new file mode 100644
index 000000000..9673e984a
Binary files /dev/null and b/src/images/ab-test/entity-metadata.png differ
diff --git a/src/images/ab-test/first-chart.png b/src/images/ab-test/first-chart.png
new file mode 100644
index 000000000..479ff7932
Binary files /dev/null and b/src/images/ab-test/first-chart.png differ
diff --git a/src/images/ab-test/grid-screenshot.png b/src/images/ab-test/grid-screenshot.png
new file mode 100644
index 000000000..88768a574
Binary files /dev/null and b/src/images/ab-test/grid-screenshot.png differ
diff --git a/src/images/ab-test/headings-screenshot.png b/src/images/ab-test/headings-screenshot.png
new file mode 100644
index 000000000..7bf3c94de
Binary files /dev/null and b/src/images/ab-test/headings-screenshot.png differ
diff --git a/src/images/ab-test/icon.png b/src/images/ab-test/icon.png
new file mode 100644
index 000000000..dde8915d6
Binary files /dev/null and b/src/images/ab-test/icon.png differ
diff --git a/src/images/ab-test/incorrect-auth-header.png b/src/images/ab-test/incorrect-auth-header.png
new file mode 100644
index 000000000..bd2523677
Binary files /dev/null and b/src/images/ab-test/incorrect-auth-header.png differ
diff --git a/src/images/ab-test/manage-access.png b/src/images/ab-test/manage-access.png
new file mode 100644
index 000000000..c8f62a86f
Binary files /dev/null and b/src/images/ab-test/manage-access.png differ
diff --git a/src/images/ab-test/nav-to-apps.png b/src/images/ab-test/nav-to-apps.png
new file mode 100644
index 000000000..f306c128d
Binary files /dev/null and b/src/images/ab-test/nav-to-apps.png differ
diff --git a/src/images/ab-test/nav-to-data-explorer.png b/src/images/ab-test/nav-to-data-explorer.png
new file mode 100644
index 000000000..5e96f8df1
Binary files /dev/null and b/src/images/ab-test/nav-to-data-explorer.png differ
diff --git a/src/images/ab-test/nerdsletter-success.png b/src/images/ab-test/nerdsletter-success.png
new file mode 100644
index 000000000..5c5fd5983
Binary files /dev/null and b/src/images/ab-test/nerdsletter-success.png differ
diff --git a/src/images/ab-test/new-message.png b/src/images/ab-test/new-message.png
new file mode 100644
index 000000000..1b8c531db
Binary files /dev/null and b/src/images/ab-test/new-message.png differ
diff --git a/src/images/ab-test/newsletter-apm.png b/src/images/ab-test/newsletter-apm.png
new file mode 100644
index 000000000..bc638e60a
Binary files /dev/null and b/src/images/ab-test/newsletter-apm.png differ
diff --git a/src/images/ab-test/newsletter-transactions.png b/src/images/ab-test/newsletter-transactions.png
new file mode 100644
index 000000000..64112e677
Binary files /dev/null and b/src/images/ab-test/newsletter-transactions.png differ
diff --git a/src/images/ab-test/nr1-app-subscription-totals.png b/src/images/ab-test/nr1-app-subscription-totals.png
new file mode 100644
index 000000000..03f225563
Binary files /dev/null and b/src/images/ab-test/nr1-app-subscription-totals.png differ
diff --git a/src/images/ab-test/nrql-query-chart.png b/src/images/ab-test/nrql-query-chart.png
new file mode 100644
index 000000000..3b18fd92f
Binary files /dev/null and b/src/images/ab-test/nrql-query-chart.png differ
diff --git a/src/images/ab-test/page-views-charts.png b/src/images/ab-test/page-views-charts.png
new file mode 100644
index 000000000..5e2a1c9a0
Binary files /dev/null and b/src/images/ab-test/page-views-charts.png differ
diff --git a/src/images/ab-test/page-views-time-range.png b/src/images/ab-test/page-views-time-range.png
new file mode 100644
index 000000000..1cd9f59ff
Binary files /dev/null and b/src/images/ab-test/page-views-time-range.png differ
diff --git a/src/images/ab-test/past-tests-after.png b/src/images/ab-test/past-tests-after.png
new file mode 100644
index 000000000..aedc29e46
Binary files /dev/null and b/src/images/ab-test/past-tests-after.png differ
diff --git a/src/images/ab-test/past-tests-before.png b/src/images/ab-test/past-tests-before.png
new file mode 100644
index 000000000..2de7f3744
Binary files /dev/null and b/src/images/ab-test/past-tests-before.png differ
diff --git a/src/images/ab-test/performance-button.png b/src/images/ab-test/performance-button.png
new file mode 100644
index 000000000..d2ce04101
Binary files /dev/null and b/src/images/ab-test/performance-button.png differ
diff --git a/src/images/ab-test/persist-selected-version-final.png b/src/images/ab-test/persist-selected-version-final.png
new file mode 100644
index 000000000..822a2643b
Binary files /dev/null and b/src/images/ab-test/persist-selected-version-final.png differ
diff --git a/src/images/ab-test/pie-charts.png b/src/images/ab-test/pie-charts.png
new file mode 100644
index 000000000..ae043c269
Binary files /dev/null and b/src/images/ab-test/pie-charts.png differ
diff --git a/src/images/ab-test/platform-state.png b/src/images/ab-test/platform-state.png
new file mode 100644
index 000000000..705769b15
Binary files /dev/null and b/src/images/ab-test/platform-state.png differ
diff --git a/src/images/ab-test/query-builder.png b/src/images/ab-test/query-builder.png
new file mode 100644
index 000000000..32d4caa4f
Binary files /dev/null and b/src/images/ab-test/query-builder.png differ
diff --git a/src/images/ab-test/raw-data-in-console.png b/src/images/ab-test/raw-data-in-console.png
new file mode 100644
index 000000000..8f99690a9
Binary files /dev/null and b/src/images/ab-test/raw-data-in-console.png differ
diff --git a/src/images/ab-test/real-table-chart-data.png b/src/images/ab-test/real-table-chart-data.png
new file mode 100644
index 000000000..8dbb625f0
Binary files /dev/null and b/src/images/ab-test/real-table-chart-data.png differ
diff --git a/src/images/ab-test/request-log.png b/src/images/ab-test/request-log.png
new file mode 100644
index 000000000..84b5b2e32
Binary files /dev/null and b/src/images/ab-test/request-log.png differ
diff --git a/src/images/ab-test/screenshot.png b/src/images/ab-test/screenshot.png
new file mode 100644
index 000000000..6e3b7492c
Binary files /dev/null and b/src/images/ab-test/screenshot.png differ
diff --git a/src/images/ab-test/select-apm.png b/src/images/ab-test/select-apm.png
new file mode 100644
index 000000000..7765359d4
Binary files /dev/null and b/src/images/ab-test/select-apm.png differ
diff --git a/src/images/ab-test/select-apps.png b/src/images/ab-test/select-apps.png
new file mode 100644
index 000000000..92baa6bfa
Binary files /dev/null and b/src/images/ab-test/select-apps.png differ
diff --git a/src/images/ab-test/select-launcher.png b/src/images/ab-test/select-launcher.png
new file mode 100644
index 000000000..fbd869d07
Binary files /dev/null and b/src/images/ab-test/select-launcher.png differ
diff --git a/src/images/ab-test/selected-version-in-modal.png b/src/images/ab-test/selected-version-in-modal.png
new file mode 100644
index 000000000..2c00cc2dd
Binary files /dev/null and b/src/images/ab-test/selected-version-in-modal.png differ
diff --git a/src/images/ab-test/stacked-entity.png b/src/images/ab-test/stacked-entity.png
new file mode 100644
index 000000000..615ecdf89
Binary files /dev/null and b/src/images/ab-test/stacked-entity.png differ
diff --git a/src/images/ab-test/subscribe.png b/src/images/ab-test/subscribe.png
new file mode 100644
index 000000000..ca58125cd
Binary files /dev/null and b/src/images/ab-test/subscribe.png differ
diff --git a/src/images/ab-test/subscription-total-billboard.png b/src/images/ab-test/subscription-total-billboard.png
new file mode 100644
index 000000000..043b7eb30
Binary files /dev/null and b/src/images/ab-test/subscription-total-billboard.png differ
diff --git a/src/images/ab-test/subscription-totals.png b/src/images/ab-test/subscription-totals.png
new file mode 100644
index 000000000..dcb1d9641
Binary files /dev/null and b/src/images/ab-test/subscription-totals.png differ
diff --git a/src/images/ab-test/table-charts.png b/src/images/ab-test/table-charts.png
new file mode 100644
index 000000000..8a505198e
Binary files /dev/null and b/src/images/ab-test/table-charts.png differ
diff --git a/src/images/ab-test/time-picked.png b/src/images/ab-test/time-picked.png
new file mode 100644
index 000000000..d9fb2a21e
Binary files /dev/null and b/src/images/ab-test/time-picked.png differ
diff --git a/src/images/ab-test/time-picker.png b/src/images/ab-test/time-picker.png
new file mode 100644
index 000000000..63b0ca4cb
Binary files /dev/null and b/src/images/ab-test/time-picker.png differ
diff --git a/src/images/ab-test/unsubscribe.png b/src/images/ab-test/unsubscribe.png
new file mode 100644
index 000000000..f8ce515ce
Binary files /dev/null and b/src/images/ab-test/unsubscribe.png differ
diff --git a/src/images/ab-test/update-api-token-button.png b/src/images/ab-test/update-api-token-button.png
new file mode 100644
index 000000000..bbb24fe37
Binary files /dev/null and b/src/images/ab-test/update-api-token-button.png differ
diff --git a/src/images/ab-test/version-descriptions.png b/src/images/ab-test/version-descriptions.png
new file mode 100644
index 000000000..30c9ebed8
Binary files /dev/null and b/src/images/ab-test/version-descriptions.png differ
diff --git a/src/images/ab-test/your-apps.png b/src/images/ab-test/your-apps.png
new file mode 100644
index 000000000..26509c3bb
Binary files /dev/null and b/src/images/ab-test/your-apps.png differ
diff --git a/src/layouts/EmbedLayout.js b/src/layouts/EmbedLayout.js
new file mode 100644
index 000000000..10f865cac
--- /dev/null
+++ b/src/layouts/EmbedLayout.js
@@ -0,0 +1,28 @@
+import React, { useEffect } from 'react';
+import { Layout } from '@newrelic/gatsby-theme-newrelic';
+import PropTypes from 'prop-types';
+import { css } from '@emotion/core';
+import useDarkMode from 'use-dark-mode';
+
+const EmbedLayout = ({ children }) => {
+ // Disable dark mode for embed pages. We want to set it to light mode
+ // and not use/update user's site darkMode preference in localstorage
+ const { disable } = useDarkMode(false, { storageKey: 'embedDarkMode' });
+ useEffect(() => disable(), [disable]);
+ return (
+
+ {children}
+
+ );
+};
+
+EmbedLayout.propTypes = {
+ children: PropTypes.node,
+};
+
+export default EmbedLayout;
diff --git a/src/layouts/MainLayout.js b/src/layouts/MainLayout.js
index cc5e90613..a5f68a85b 100644
--- a/src/layouts/MainLayout.js
+++ b/src/layouts/MainLayout.js
@@ -59,16 +59,7 @@ const MainLayout = ({ children, pageContext }) => {
`}
to="/"
>
-
+ {
+ const isEmbed = pageContext.layout && pageContext.layout === 'EmbedLayout';
if (pageContext.fileRelativePath.match(/404/)) {
return children;
}
+ if (isEmbed) {
+ return {children};
+ }
return {children};
};
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/catalog-info.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/catalog-info.mdx
new file mode 100644
index 000000000..3fccc689a
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/catalog-info.mdx
@@ -0,0 +1,124 @@
+---
+path: '/build-apps/ab-test/catalog'
+title: 'Describe your app for the catalog'
+template: 'GuideTemplate'
+description: 'Describe your app for the catalog'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add navigation to your nerdlet_](../navigation), before starting this one.
+
+
+
+In the last lesson, you finished the A/B test application you've been building throughout this course. Now, it's time to prepare it for publication. When you publish your app to the New Relic One catalog, users can view it and subscribe to it. You can help your users by showing and telling them what the app does, how to use it, and more.
+
+## Create catalog information
+
+To supply information to the catalog about your app, you need to create the _catalog_ directory in your nerdpack.
+
+
+
+
+
+Navigate to the root of your Nerdpack at `nru-programmability-course/describe-app/ab-test`.
+
+
+
+
+
+Create the _catalog_ directory:
+
+```sh
+nr1 create -t catalog
+```
+
+This creates a _catalog_ directory with template files for inputting custom information about your application.
+
+
+
+Read [our documentation](../../publish-deploy#add-images-and-metadata-to-your-apps) to learn more about the catalog directory.
+
+
+
+
+
+
+
+Update _catalog/documentation.md_:
+
+```md
+## Documentation
+
+This application combines and presents data from New Relic and the Nerdsletter API so that we can make an informed decision on which design version results in more high-quality newsletter subscriptions.
+
+It also presents an button to end the test, which saves the date and version descriptions in local storage.
+
+We can use this app for future A/B tests as well!
+```
+
+For this course, you're keeping the documentation concise by merely describing what the app does. In your own projects, you might also include instructions for contributing to the project in an open source context, installing the app's dependencies, or anything else that might help a user navigate complexities in your app or code.
+
+
+
+
+
+Update _catalog/config.json_:
+
+```json
+{
+ "tagline": "Win @ newsletter subscriptions",
+ "details": "Display test data for our newsletter subscription A/B test",
+ "repository": "https://github.com/newrelic-experimental/nru-programmability-course",
+ "whatsNew": "Initial release! Includes:\n- A variety of charts for understanding the test results\n- An end test button for storing some test data in a table",
+ "support": {
+ "email": {
+ "address": ""
+ },
+ "issues": {
+ "url": ""
+ },
+ "community": {
+ "url": ""
+ }
+ }
+}
+```
+
+Here, you've specified a tagline, project details, the source code repository, and a short list of everything included in the initial release. In other projects, you might also add a support email or support links.
+
+
+
+
+
+Save the following screenshot to your _catalog/screenshots_ directory:
+
+![App screenshot](../../../images/ab-test/screenshot.png)
+
+Users will be able to see a carousel of screenshots in your app's **About** page in the catalog.
+
+
+
+
+
+Save the following icon to the root of your Nerdpack at _nru-programmability-course/describe-app/ab-test_:
+
+![App icon](../../../images/ab-test/icon.png)
+
+This will act as the launcher's icon and the app's catalog icon on the **About** page.
+
+
+
+
+
+Now you've added documentation, screenshots, metadata, and an icon to your app so that users will be able to understand what it does and why they might need it. For now, these files are all local and they need to be submitted to the catalog. But before you can do that, your app, itself, needs to be published to the catalog.
+
+In the next lesson, you'll publish your app, submit your catalog information, and view the results in New Relic.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Publish your New Relic One application_](../publish).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/chart-components-intro.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/chart-components-intro.mdx
new file mode 100644
index 000000000..7a4a7e480
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/chart-components-intro.mdx
@@ -0,0 +1,30 @@
+---
+path: '/build-apps/ab-test/add-charts'
+title: 'Add chart components to your A/B test application'
+template: 'GuideTemplate'
+description: 'Add chart components to your A/B test application'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Serve your New Relic One application_](../serve-app), before starting this one.
+
+
+
+The New Relic One application that you're building throughout this course allows developers to A/B test their websites. To run a successful A/B test, site owners need to be able to analyze the results. Without knowing how each design performs, how will developers determine which design to use?
+
+Here is a mockup for the A/B test application you're building:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+Your application displays data for two competing versions. With various charts and tables, your users will be able to analyze the results of their test and make informed decisions on which design will work best to achieve their goals.
+
+You’ll refer back to this mockup many times throughout this course. It shows you what charts to create, how to organize them, and what kind of data to provide them. In the next four sections, you learn how to create every chart that your A/B test application needs to operate effectively. After that, you’ll arrange the charts to match the mockup’s design and supply New Relic data collected from the demo applications you spun up earlier.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Add your first chart_](../first-chart).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/chart-group.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/chart-group.mdx
new file mode 100644
index 000000000..8d7e0dbf8
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/chart-group.mdx
@@ -0,0 +1,207 @@
+---
+path: '/build-apps/ab-test/chart-group'
+title: 'Add a chart group'
+template: 'GuideTemplate'
+description: 'Add a chart group'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add tables_](../table-charts), before starting this one.
+
+
+
+In previous lessons, you added a variety of charts to your A/B test application. These charts presented different facets of information about your A/B test, and they behaved independently from one other. In this lesson, you’ll create a new pair of line charts and learn how to synchronize their behaviors. Before you write any new code, consult your design guide to understand the role that these new charts will play in your application:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+In the design guide, there is a line graph under each of the tables you created in the last lesson. These line graphs show the version-specific response times for newsletter signup requests. Now, you’ll build two more line charts, but this time, you’ll group them with a `ChartGroup` and specify that their values are measured in milliseconds.
+
+
+
+
+
+Change to the _add-a-chart-group_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-a-chart-group
+```
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet_, add a new Javascript file named _page-views.js_:
+
+```sh
+touch page-views.js
+```
+
+
+
+
+
+In this new file, create a component called `VersionPageViews` to hold a `LineChart`, which shows the number of times a page is viewed:
+
+```js fileName=nerdlets/ab-test-nerdlet/page-views.js
+import React from 'react';
+import { LineChart } from 'nr1';
+
+export default class VersionPageViews extends React.Component {
+ render() {
+ const versionPageViews = {
+ metadata: {
+ id: `page-views-${this.props.version}`,
+ name: `Version ${this.props.version.toUpperCase()}`,
+ viz: 'main',
+ color: 'blue',
+ units_data: {
+ y: 'MS'
+ }
+ },
+ data: [
+ { x: 0, y: 10 },
+ { x: 10, y: 13 },
+ { x: 20, y: 11.5 },
+ { x: 30, y: 10 },
+ { x: 40, y: 8.75 },
+ { x: 50, y: 9 },
+ ],
+ }
+ return
+ }
+}
+```
+
+Notice the new attribute in the series' `metadata` fields: `units_data`. This attribute describes the unit type for a specified data axis. In this case, you set the unit type for the y axis to `'MS'`, which stands for milliseconds. Other options for unit types, include: `'PERCENTAGE'`, `'TIMESTAMP'`, and `'BYTES_PER_SECOND'`.
+
+
+
+
+
+In your Nerdlet's _index.js_ file, import your new component and update your Nerdlet's `render()` method:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=6,17-18
+import React from 'react';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+
+
+
+
+
+
+
+
+
+ }
+}
+
+```
+
+
+
+
+
+In _index.js_, import `ChartGroup` from `nr1` and group your `VersionPageViews`:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=2,18-21
+import React from 'react';
+import { ChartGroup } from 'nr1';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+
+
+
+
+
+
+
+
+
+
+
+ }
+}
+
+```
+
+Because the tables are conceptually related, as they show contrasting performance metrics over the same timeslice, it makes sense to group them in a `ChartGroup`. This means that the two charts behave synchronously. For example, when you hover over one chart, the other chart shows a hover indicator at the same x coordinate.
+
+
+
+
+
+Navigate to the root of your Nerdpack at `nru-programmability-course/add-a-chart-group/ab-test`.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+View your changes in [New Relic](https://one.newrelic.com?nerdpacks=local):
+
+![Your app with synced line charts](../../../images/ab-test/chart-group-syncing.png)
+
+Here, you see the `LineChart` components synchronized in your application.
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+Each of the chart component types you've used in this lesson have had different series configurations. Most chart components share the same `metadata` attributes, like `LineChart` and `PieChart`, but differ in their `data` formats.
+
+It's helpful to be aware of the different `data` formats, for when you create your own charts. You can read more about the `data` formats in the [chart component documentation](https://developer.newrelic.com/components/charts#data).
+
+
+
+
+
+
+
+Now your application is filled with charts, but it doesn't look great. The charts are stacked on top of one another in an unhelpful way. In the next lesson, you'll learn about the user interface components from the SDK and how you can use them to organize your charts.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. When you're ready, continue on to the next lesson: [_Add user interface components to your application_](../add-ui).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/chart-headings.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/chart-headings.mdx
new file mode 100644
index 000000000..a2928723d
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/chart-headings.mdx
@@ -0,0 +1,330 @@
+---
+path: '/build-apps/ab-test/chart-headings'
+title: 'Add chart headings'
+template: 'GuideTemplate'
+description: 'Add chart headings'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add a grid_](../grid), before starting this one.
+
+
+
+Organizing charts is one way to clarify the information you're trying to convey to your users. Headings are another. Since you have several charts on the same page, use headings to describe the information they show. To remember what data each chart presents, consult your design guide:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+The design guide details what data each chart shows. Use those details to craft useful headings for your charts.
+
+
+
+
+
+Change to the _add-chart-headings_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-chart-headings
+```
+
+
+
+
+
+In _newsletter-signups.js_, _page-views.js_, _past-tests.js_, _total-cancellations.js_, _total-subscriptions.js_, and _totals.js_, import `HeadingText` and add a descriptive heading above the chart in each of your custom components:
+
+<>
+
+```js fileName=nerdlets/ab-test-nerdlet/newsletter-signups.js lineHighlight=2,38-43
+import React from 'react';
+import { HeadingText, LineChart } from 'nr1';
+
+export default class NewsletterSignups extends React.Component {
+ render() {
+ const versionASignups = {
+ metadata: {
+ id: 'version-a-newsletter-signups',
+ name: 'Version A',
+ viz: 'main',
+ color: 'blue',
+ },
+ data: [
+ { x: 0, y: 0 },
+ { x: 10, y: 10 },
+ { x: 20, y: 15 },
+ { x: 30, y: 5 },
+ { x: 40, y: 30 },
+ { x: 50, y: 25 },
+ ],
+ }
+ const versionBSignups = {
+ metadata: {
+ id: 'version-b-newsletter-signups',
+ name: 'Version B',
+ viz: 'main',
+ color: 'green',
+ },
+ data: [
+ { x: 0, y: 20 },
+ { x: 10, y: 5 },
+ { x: 20, y: 25 },
+ { x: 30, y: 45 },
+ { x: 40, y: 50 },
+ { x: 50, y: 35 },
+ ],
+ }
+ return
+
+ Version {this.props.version.toUpperCase()} - Page views vs. subscriptions
+
+
+
+ }
+}
+```
+
+Notice the new `
` tag in each `render()`, which allows you to return multiple elements at once. Also, remember that we provided you a CSS stylesheet that styles the `chartHeader` class. You can look at `nerdlets/ab-test-nerdlet/styles.scss` to see more.
+
+You don't need to modify your Nerdlet's `render()` method in _index.js_ because your components encapsulate the new code.
+
+>
+
+
+
+
+
+Navigate to the root of your Nerdpack at `nru-programmability-course/add-a-grid/ab-test`.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+View your changes in [New Relic](https://one.newrelic.com?nerdpacks=local):
+
+![Your application with chart headings](../../../images/ab-test/headings-screenshot.png)
+
+Here, you see that your charts have descriptive headings.
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+Well done! You've created all the charts that are laid out in your design guide. You’ve also organized them into a neat grid and added headings so that the charts are intelligible. While chart headings helps you understand what each chart shows, it'd be nice to add descriptions to help you remember what each version in your A/B test represents. In the next lesson, you'll add a description for each design version.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Add version descriptions_](../version-descriptions).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/confirmation-modal.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/confirmation-modal.mdx
new file mode 100644
index 000000000..7326f7649
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/confirmation-modal.mdx
@@ -0,0 +1,1052 @@
+---
+path: '/build-apps/ab-test/confirmation-modal'
+title: 'Present an end test confirmation modal'
+template: 'GuideTemplate'
+description: 'Present an end test confirmation modal'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Persist the selected version_](../persist-version), before starting this one.
+
+
+
+In this tutorial, you're building an A/B test application. The application surfaces data about an ongoing A/B test on your website, which you then use to decide which page design version is most effective at engaging users. As part of that overarching goal, you're building a section which allows you to end the test by deciding a winner of the experiment:
+
+![First version of your end test section](../../../images/ab-test/end-test-section-v1.png)
+
+Unfortunately, there are a few issues with the code and design for this section. By the end of this course, pressing _End test_ will tell your website's backend server that every customer should see the version selected here. This is destructive behavior because it makes an irreversible change to your website. To account for the desctructiveness of pressing _End test_, you need to modify some features of your application:
+
+- Make the button look important to catch your eye
+- Prompt yourself with a confirmation message before ending the test to ensure you don't end it prematurely
+
+## Show and hide your confirmation modal
+
+
+
+
+
+Change to the _present-confirmation-modal_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/present-confirmation-modal
+```
+
+This directory contains the code your application should have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet/end-test.js_, update the `Button` to use the `DESTRUCTIVE` styling:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=27
+import React from 'react';
+import {
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ render() {
+ return
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+
+
+Read more about [`Button`](https://developer.newrelic.com/components/button) and its props on the New Relic developer site.
+
+
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/present-confirmation-modal/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+[View your application](https://one.newrelic.com?nerdpacks=local):
+
+![Second version of your end test section](../../../images/ab-test/end-test-section-v2.png)
+
+Now you've styled your button to convey its destructive consequences, but that's not enough to prevent you from accidentally clicking it. Next, you create a shield for your website's backend. This extra layer of protection requires you to confirm that you intend to end the test before actually doing so.
+
+
+
+
+
+Add a `Modal` to `EndTestButton`:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=3,8,31-43
+import React from 'react';
+import {
+ BlockText,
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Modal,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version A
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+Your new modal contains a heading, a confirmation message, the winning design version, and two buttons. You'll explore some of these components later in this course.
+
+With your nerdpack served locally, [view your application](https://one.newrelic.com?nerdpacks=local) to see your new `Modal`:
+
+![End test modal](../../../images/ab-test/end-test-modal.png)
+
+
+
+Remember, while your server is running, it will update automatically when you save your code.
+
+
+
+The modal looks great, but it has three issues:
+
+- It was present before you clicked _End test_
+- You can't dismiss it
+- It always says you selected "Version A", even if you didn't
+
+In the following steps, you'll correct all three of these issues.
+
+
+
+
+
+In `EndTestButton`, initialize `state` with a value for `modalHidden`:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=27-33
+import React from 'react';
+import {
+ BlockText,
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Modal,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ modalHidden: true,
+ }
+ }
+
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version A
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+`modalHidden` determines whether or not to hide the modal. The default value of `modalHidden` is `true` because you only want to show the modal when you select **End test**.
+
+
+
+
+
+Supply `modalHidden` to the `Modal`:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=39
+import React from 'react';
+import {
+ BlockText,
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Modal,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ modalHidden: true,
+ }
+ }
+
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version A
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+First, you've created a constructor to accept component props. This provides your component access to the `modalHidden` prop that you passed in `EndTestSection`. Then you provide the value of `modalHidden` to the `Modal` component's `hidden` prop.
+
+
+
+Read more about `Modal` and its props on the [New Relic developer site](https://developer.newrelic.com/components/modal).
+
+
+
+
+
+
+
+Add and bind two new methods to `EndTestSection`:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=34-35,38-44
+import React from 'react';
+import {
+ BlockText,
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Modal,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ modalHidden: true,
+ }
+
+ this.showModal = this.showModal.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ }
+
+ closeModal() {
+ this.setState({ modalHidden: true });
+ }
+
+ showModal() {
+ this.setState({ modalHidden: false });
+ }
+
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version A
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+Use `closeModal` and `showModal` to close and show your modal, respectively, depending on how a user interacts with the modal.
+
+
+
+
+
+Show the modal when you click **End test**:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=48
+import React from 'react';
+import {
+ BlockText,
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Modal,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ modalHidden: true,
+ }
+
+ this.showModal = this.showModal.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ }
+
+ closeModal() {
+ this.setState({ modalHidden: true });
+ }
+
+ showModal() {
+ this.setState({ modalHidden: false });
+ }
+
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version A
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+Here, you've supplied `showModal()` as the callback for the `Button` component's `onClick` event.
+
+[View your application](https://one.newrelic.com?nerdpacks=local) where the modal is hidden by default. Click **End test** to see the modal.
+
+
+
+
+
+Close the modal from the `Modal` component's `onClose` callback and from the **Yes, end test** and **No, continue test** buttons:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=50,60,61
+import React from 'react';
+import {
+ BlockText,
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Modal,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ modalHidden: true,
+ }
+
+ this.showModal = this.showModal.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ }
+
+ closeModal() {
+ this.setState({ modalHidden: true });
+ }
+
+ showModal() {
+ this.setState({ modalHidden: false });
+ }
+
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version A
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+[View your application](https://one.newrelic.com?nerdpacks=local) and click **End test**. The modal opens and displays your confirmation message. Close the modal by clicking **X** at the top right, either of its buttons, or even in the dark space to its left.
+
+Notice that the modal says you chose "Version A", even if you choose "Version B". This is because "Version A" is hardcoded in the `Modal` text. Next, you'll make that dynamic.
+
+
+
+
+
+## Use the version you selected in your modal
+
+
+
+
+
+In `EndTestSection`, pass `selectedVersion` to the `EndTestButton`:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=96
+import React from 'react';
+import {
+ BlockText,
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Modal,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ modalHidden: true,
+ }
+
+ this.showModal = this.showModal.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ }
+
+ closeModal() {
+ this.setState({ modalHidden: true });
+ }
+
+ showModal() {
+ this.setState({ modalHidden: false });
+ }
+
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version A
+
+
+
+
+
+
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version {this.props.selectedVersion}
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+[View your application](https://one.newrelic.com?nerdpacks=local) and select "Version B" from the menu. Click **End test** to see "Version B" in the confirmation modal:
+
+![The modal shows your selected version](../../../images/ab-test/selected-version-in-modal.png)
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+Congratulations! You've done a lot of work and it shows in the usefulness of your application. You've created charts that show mocked data. You've organized your charts into a readable structure. You've added an interface for your users to interact with the test. Now, you need some real data. In the next lesson, you'll replace the mocked data in your charts with real data from your backend service.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Add NrqlQuery components to your nerdlet_](../nrql).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/create-nerdpack.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/create-nerdpack.mdx
new file mode 100644
index 000000000..8758d5fc4
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/create-nerdpack.mdx
@@ -0,0 +1,140 @@
+---
+path: '/build-apps/ab-test/create-nerdpack'
+title: 'Create a Nerdpack'
+template: 'GuideTemplate'
+description: 'Create a Nerdpack'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Install and configure the New Relic One CLI_](../install-nr1), before starting this one.
+
+
+
+A Nerdpack is a package that contains all the files that make up a New Relic One application. Nerdpacks include files for metadata, Nerdlets, assets, and more. To create a Nerdpack, use the New Relic One CLI:
+
+```bash
+nr1 create --type nerdpack
+```
+
+When `nr1` prompts for a component name, use "ab-test" and watch the tool create the foundations of your Nerdpack. Once it's finished, explore the files it created for you.
+
+## Root directory
+
+At the root level, you have a directory called _ab-test_. Everything inside this directory is part of the Nerdpack:
+
+```bash
+ls ab-test
+[output] README.md {blue}nerdlets{plain} nr1.json package.json
+[output] {blue}launchers node_modules{plain} package-lock.json
+```
+
+`Readme.md` provides instructions for creating Nerdpack elements and serving your application locally. This and `nr1 --help` are useful if you forget how to interact with your Nerdpack using the command line tool.
+
+### Javascript files
+
+_package.json_, _package-lock.json_, and *node_modules* are important for running your JavaScript application, but aren't unique to Nerdpacks. You can learn about these modules from JavaScript courses should you need to tweak them. For now, take a look at _nr1.json_, one of the most relevant files in this directory.
+
+### Metadata file
+
+_nr1.json_ is the Nerdpack's metadata file, containing a schema type, unique identifier, a display name, and a description. Since you’re building a New Relic One application for running and analyzing A/B tests, update the package's `displayName` to "A/B Test" and set the description to "A/B test your application using New Relic One":
+
+```json fileName=nr1.json
+{
+ "schemaType": "Nerdpack",
+ "id": "311bcd0c-f7eb-4285-afed-4219179bf91d",
+ "displayName": "A/B Test",
+ "description": "A/B test your application using New Relic One."
+}
+```
+
+It's good to describe your application's purpose, especially if you're going to publish your application for others to use.
+
+Next, look at the _launchers_ and _nerdlets_ subdirectories.
+
+## Launchers
+
+_launchers_ is a directory because you can create multiple launchers for your New Relic One application. `nr1 create` only created one launcher for your Nerdpack and called it "ab-test-launcher". Inside its directory, there are two files:
+
+- _icon.png_ is an image that represents the application
+- _nr1.json_ is the launcher's metadata file
+
+Use "A/B Test Launcher" for the launcher's `displayName` and "Open the A/B test Nerdlet" for the `description` in _nr1.json_:
+
+```json fileName=launchers/ab-test-launcher/nr1.json
+{
+ "schemaType": "LAUNCHER",
+ "id": "ab-test-launcher",
+ "displayName": "A/B Test Launcher",
+ "description": "Open the A/B test Nerdlet",
+ "rootNerdletId": "ab-test-Nerdlet"
+}
+```
+
+Notice that the launcher has a `rootNerdletId`. This identifies the Nerdlet that the launcher opens when it's selected. This Nerdpack has only one Nerdlet, called `ab-test-nerdlet`, but some Nerdpacks may have multiple. Make sure that you set the `rootNerdletId` to the Nerdlet you want the launcher to open.
+
+## Nerdlets
+
+The _nerdlets_ directory contains all the Nerdlets that make up your New Relic One application. Since `ab-test-nerdlet` is the only Nerdlet in this Nerdpack, there is only one subdirectory. In _nerdlets/ab-test-nerdlet_, there are three files:
+
+- _index.js_ is the JavaScript file that contains your Nerdlet component
+- _styles.scss_ holds the Sass stylesheet for your Nerdlet
+- _nr1.json_ contains the Nerdlet's metadata
+
+Update your Nerdlet's `displayName` to "A/B Test" and `description` to "Control and view results of your A/B test":
+
+```json fileName=nerdlets/ab-test-nerdlet/nr1.json
+{
+ "schemaType": "Nerdlet",
+ "id": "ab-test-Nerdlet",
+ "displayName": "A/B Test",
+ "description": "Control and view results of your A/B test"
+}
+```
+
+Now, you've customized your Nerdpack, Nerdlet, and launcher with informative display names and descriptions, but what does your application actually do? Check out _index.js_ to see what your default Nerdlet looks like:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js
+import React from 'react';
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
Hello, ab-test-nerdlet Nerdlet!
;
+ }
+}
+```
+
+Here, you have `AbTestNerdletNerdlet`, which `nr1` created for you. It's a React component that renders a first-level welcome heading. Because this is the root Nerdlet, as specified in your launcher's _nr1.json_ file, when you click on your launcher in the New Relic platform, the launcher will open a view that displays this heading.
+
+
+
+In this lesson, you used `nr1 create` to create a Nerdpack, complete with a launcher and a Nerdlet. If you want to create another Nerdlet or Launcher in your Nerdpack, you can also do that with `nr1 create`:
+
+```sh
+nr1 create --type nerdlet
+nr1 create --type launcher
+```
+
+You can even skip the `--type` flag and select an option from a list:
+
+```sh
+nr1 create
+[output] {green}?{plain} What kind of component do you want to create? {muted}› - Use arrow-keys. Return to submit.
+[output] nerdpack
+[output] {green}❯ nerdlet{muted} - create a/an Nerdlet artifact inside your Nerdpack.
+[output] launcher
+[output] catalog
+[output] visualization
+```
+
+
+
+In the next lesson, you'll learn how to serve your Nerdpack locally and see your app in action!
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Serve your New Relic One application_](../serve-app).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/demo-setup.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/demo-setup.mdx
new file mode 100644
index 000000000..dc064da76
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/demo-setup.mdx
@@ -0,0 +1,100 @@
+---
+path: '/build-apps/ab-test/demo-setup'
+title: 'Spin up your demo services'
+template: 'GuideTemplate'
+description: 'Spin up your demo services'
+duration: '5 min'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+
+
+Before you build your New Relic One application, you need to spin up your demo services. This coursework depends on two important services:
+
+- A web service that shows a newsletter signup form. The form's heading text alternates between two versions because you're performing an A/B test to determine which text leads to more high-quality subscriptions.
+- A simulator service that sends steady traffic to the website so that you don't have to manually generate data
+
+To spin up your demo services, you first need to install [Docker](https://docs.docker.com/get-docker/) and [Docker compose](https://docs.docker.com/compose/install/).
+
+## Spin up your demo services
+
+
+
+
+
+Clone the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course) from GitHub:
+
+```sh
+git clone https://github.com/newrelic-experimental/nru-programmability-course
+```
+
+This repository contains code for creating NodeJS New Relic automations. It also contains a an app code directory for each lesson in the course. You'll use these directories to follow along with the course content.
+
+
+
+
+
+Change to the demo directory, called `ab-test-app`:
+
+```sh
+cd nru-programmability-course/ab-test-app
+```
+
+This directory contains configuration files and READMEs for using the demo.
+
+
+
+
+
+Build and run the web service and simulator containers, using `docker-compose`:
+
+```sh
+NEW_RELIC_LICENSE_KEY= docker-compose up -d
+```
+
+
+
+Make sure you replace `` with your actual [license key](https://docs.newrelic.com/docs/accounts/accounts-billing/account-setup/new-relic-license-key/).
+
+
+
+Once it's finished, view the website at localhost:3001:
+
+![Demo service](../../../images/ab-test/demo-service.png)
+
+
+
+
+
+Visit [New Relic](https://one.newrelic.com), and navigate to **APM** in the top navigation menu. Select **Newsletter** from the list of instrumented services:
+
+![Newsletter APM service](../../../images/ab-test/newsletter-apm.png)
+
+You may have to wait a minute or two for New Relic to recieve data from your demo services. Once it's ready, you'll see transaction data, such as performance, throughput, and an Apdex score:
+
+![Newsletter APM transactions](../../../images/ab-test/newsletter-transactions.png)
+
+
+
+
+
+To spin down your demo services, run `docker-compose down` from your `ab-test-app` directory:
+
+```sh
+docker-compose down
+```
+
+
+
+
+
+Now you're ready to build your New Relic One application! The first step is to [install and configure the New Relic One CLI](../install-nr1).
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Install and configure the New Relic One CLI_](../install-nr1).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/end-test.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/end-test.mdx
new file mode 100644
index 000000000..b6383ecc5
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/end-test.mdx
@@ -0,0 +1,264 @@
+---
+path: '/build-apps/ab-test/end-test'
+title: 'Add a section to end your test'
+template: 'GuideTemplate'
+description: 'Add a section to end your test'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add version descriptions_](../version-descriptions), before starting this one.
+
+
+
+In this course, you’re building an A/B test application in New Relic. The application presents a lot of data, through its charts, about the effectiveness of each design version you’re testing on your demo website. Ultimately, you’ll be able to use that data to decide which design is most effective and show that design to every user who visits your site. In this lesson, you’ll build a form into your application that lets you choose which design you want to show to every user of your site. Before writing any code, look at your design guide to review the section you’re going to build:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+This new section has three main components:
+
+- A heading with instructional copy: "Pick a version to end the test"
+- A component that you use to crown the winning version of the A/B test
+- A button to confirm the winner that you selected
+
+
+
+
+
+Change to the _add-end-test-section_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-end-test-section
+```
+
+
+
+
+In _nerdlets/ab-test-nerdlet_, add a new Javascript file named _end-test.js_:
+
+```sh
+touch end-test.js
+```
+
+
+
+
+
+In this new file, create a `VersionSelector` component to encapsulate a `Select` and its `SelectItem` child components:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js
+import React from 'react';
+import { Select, SelectItem } from 'nr1';
+
+class VersionSelector extends React.Component {
+ render() {
+ return
+ }
+}
+```
+
+`VersionSelector` renders a `Select` component with two choices. In each `SelectItem`, you specify a `value` prop. In this case, you use `'A'` to represent version A and `'B'` to represent version B.
+
+
+
+
+
+Create another component for a `Button` you'll use to make your test as ended:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=2,13-19
+import React from 'react';
+import { Button, Select, SelectItem } from 'nr1';
+
+class VersionSelector extends React.Component {
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ render() {
+ return
+
+
+ }
+}
+```
+
+This looks trivial, but it will encapsulate button logic as you iterate on your app code.
+
+
+
+
+
+Create a final component for the entire section you'll use to mark the end of your test:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=4-6,28-44
+import React from 'react';
+import {
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ render() {
+ return
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+Here, you create a `Grid` with three items. First, you create a `GridItem` that contains a `HeadingText` and spans all 12 columns. In the next row, you have two items:
+
+- The `VersionSelector` component you created in the previous step
+- A `Button` which reads "End test"
+
+These items each span one column, but instead of using `columnSpan`, they use a combination of `columnStart` and `columnEnd` to specify which columns they cover.
+
+
+
+
+
+In your Nerdlet's _index.js_ file, add `EndTestSection` to your nerdlet:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=3,45
+import React from 'react';
+import { ChartGroup, Grid, GridItem } from 'nr1';
+import EndTestSection from './end-test';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionDescription from './description';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+const VERSION_A_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter"'
+const VERSION_B_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter and get a free shirt!"'
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+}
+```
+
+
+
+
+
+Navigate to the root of your Nerdpack at `nru-programmability-course/add-end-test-section/ab-test`.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+[View your changes](https://one.newrelic.com?nerdpacks=local):
+
+![First version of your end test section](../../../images/ab-test/end-test-section-v1.png)
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+However, you need to make a few improvements to this code. When you select a version, the selected value in the component doesn't change. You must control the value that `Select` displays using its `value` prop and `onChange` event handler. In the next lesson, you’ll update your code to persist your version choice in the `Select` component.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Persist the selected version_](../persist-version).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/first-chart.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/first-chart.mdx
new file mode 100644
index 000000000..5b8191750
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/first-chart.mdx
@@ -0,0 +1,166 @@
+---
+path: '/build-apps/ab-test/first-chart'
+title: 'Add your first chart'
+template: 'GuideTemplate'
+description: 'Add your first chart'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add chart components to your A/B test application_](../add-charts), before starting this one.
+
+
+
+In this course, you’re building an A/B test application for the New Relic platform. Previously, you learned about the New Relic One SDK and its component library. Now, It’s time for you to start building your application focusing first on chart components. Before you write any code, consult your design guide to remember what the application will look like when you finish:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+There are several charts you need to create, which may seem overwhelming at first, but take it one step at a time. The topmost chart, and the first you’ll create, is a line chart that shows the number of users who sign up for your newsletter and what version of your website they were shown.
+
+
+
+
+
+Change to the _add-your-first-chart_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-your-first-chart
+```
+
+This directory contains the code your application should have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet_, add a new Javascript file named _newsletter-signups.js_:
+
+```sh
+touch newsletter-signups.js
+```
+
+
+
+
+
+In this new file, create a component called `NewsletterSignups` to hold your first `LineChart` and some mock data:
+
+```js fileName=nerdlets/ab-test-nerdlet/newsletter-signups.js
+import React from 'react';
+import { LineChart } from 'nr1';
+
+export default class NewsletterSignups extends React.Component {
+ render() {
+ const versionASignups = {
+ metadata: {
+ id: 'version-a-newsletter-signups',
+ name: 'Version A',
+ viz: 'main',
+ color: 'blue',
+ },
+ data: [
+ { x: 0, y: 0 },
+ { x: 10, y: 10 },
+ { x: 20, y: 15 },
+ { x: 30, y: 5 },
+ { x: 40, y: 30 },
+ { x: 50, y: 25 },
+ ],
+ }
+ const versionBSignups = {
+ metadata: {
+ id: 'version-b-newsletter-signups',
+ name: 'Version B',
+ viz: 'main',
+ color: 'green',
+ },
+ data: [
+ { x: 0, y: 20 },
+ { x: 10, y: 5 },
+ { x: 20, y: 25 },
+ { x: 30, y: 45 },
+ { x: 40, y: 50 },
+ { x: 50, y: 35 },
+ ],
+ }
+ return
+ }
+}
+```
+
+Here, you’ve created two arbitrary data series which represent versions A and B of your test. Right now, you're using manually-crafted data points. In the future, you'll replace these with data queried from New Relic.
+
+Notice that you’ve specified a color for each series in the chart. The `color` metadata attribute takes any CSS-acceptable color format. Setting `viz: 'main'` means that you want to render the `LineChart` series as a line, the component's default display style. For the `data` prop in the `LineChart` component, you passed the two series in an array so that both are represented together. Finally, you used the chart’s `fullWidth` prop to stretch the chart’s width to fill the view.
+
+
+
+
+
+In your Nerdlet's _index.js_ file, import your new component and replace the default heading in your Nerdlet's `render()` method with your new component:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=2,6-8
+import React from 'react';
+import NewsletterSignups from './newsletter-signups';
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+
+
+ }
+}
+```
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/add-your-first-chart/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+View your changes in [New Relic](https://one.newrelic.com?nerdpacks=local):
+
+![Your first chart](../../../images/ab-test/first-chart.png)
+
+Here, you see the `LineChart` displayed in your application.
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+In seven steps, you’ve breathed life into your New Relic One application. Instead of a bland “Hello world” message, your application now shows a colorful line chart with two mocked data series. These data series represent server-side traffic for each of the competing designs in your demo website. In the next lesson, you’ll build on this foundation by creating another chart type.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. When you're ready, continue on to the next lesson: [_Add pie charts_](../pie-charts).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/grid.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/grid.mdx
new file mode 100644
index 000000000..a78818e54
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/grid.mdx
@@ -0,0 +1,138 @@
+---
+path: '/build-apps/ab-test/grid'
+title: 'Add a grid'
+template: 'GuideTemplate'
+description: 'Add a grid'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add user interface components to your application_](../chart-group), before starting this one.
+
+
+
+In previous hands-on lessons, you created all the charts from your design guide. Unfortunately, they aren’t laid out like they are in the design. The New Relic One SDK provides multiple solutions for organizing your components. The one you’ll learn about in this lesson is the `Grid` component.
+
+To arrange your charts, place them in a `Grid`. A `Grid` is a container you use to organize your content into 12-column rows. A `GridItem` is the building block for a `Grid`. You specify the `columnSpan` of a `GridItem` between _1_ and _12_ to determine how much of the row that the `GridItem` should cover. So, if you want a chart to stretch across the width of the view, you would place the chart in a `GridItem` with a `columnSpan` of _12_. If you want twelve charts to fit in a single row, use a `columnSpan` of _1_.
+
+Using the design guide as a base, you can plan how to organize your `Grid`:
+
+![Charts layout](../../../images/ab-test/charts-layout.png)
+
+Once you’ve planned out how you’re going to arrange your columns, you can write some code to realize your plans.
+
+
+
+
+
+Change to the _add-a-grid_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-a-grid
+```
+
+
+
+
+
+In your Nerdlet's _index.js_ file, import `Grid` and `GridItem`. Then, update your nerdlet's `render()` method by placing each chart in a `GrildItem`. Finally, place all your items in a `Grid`:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=2,13-28
+import React from 'react';
+import { ChartGroup, Grid, GridItem } from 'nr1';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+}
+```
+
+The `columnSpan` for each chart matches the layout plan. Every chart that takes up half the 12-column row has a `columnSpan` of _6_, and every chart that takes up the whole row has a `columnSpan` of _12_. Notice that each of the charts in the `ChartGroup`, but not the `ChartGroup` itself, is in a `GridItem`. A `ChartGroup` connects charts' behaviors, not their locations.
+
+Each chart fills its `columnSpan` because they use the `fullWidth` prop. `fullWidth` makes the chart fill its available horizontal space and `GridItem.columnSpan` restricts that space to some fraction of the row. Remove a `fullWidth` prop, and see how the chart reacts.
+
+
+
+Notice the `wrapper` class applied to the `Grid`. We quietly provided some CSS styles to help make this app look great! This isn't a CSS course, so we won't cover everything we added, we'll just include the styles we intend for you to use in the code snippets. Just know that every UI component in the New Relic One SDK provides a `className` prop where you can provide a CSS class name.
+
+You can view the styles in _nerdlets/ab-test-nerdlet/styles.scss_.
+
+
+
+
+
+
+
+Navigate to the root of your Nerdpack at `nru-programmability-course/add-a-grid/ab-test`.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+View your changes in [New Relic](https://one.newrelic.com?nerdpacks=local):
+
+![Your application with charts arranged in a grid](../../../images/ab-test/grid-screenshot.png)
+
+Here, you see your charts styled and arranged in a grid.
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+In just six steps, you significantly improved the readability and usability of your A/B test application, but you can take further steps to improve the usability of your charts. In the next lesson, you’ll add headings to your charts to remind yourself of what data each chart presents.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. When you're ready, continue on to the next lesson: [_Add chart headings_](../chart-headings).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/index.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/index.mdx
new file mode 100644
index 000000000..8500341a3
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/index.mdx
@@ -0,0 +1,29 @@
+---
+path: '/ab-test'
+title: 'Build an A/B test application'
+template: 'OverviewTemplate'
+description: ''
+redirects:
+ - /use-cases/ab-test
+---
+
+Imagine you've developed a website and instrumented it with New Relic's Browser monitoring. New Relic's core feature set provides a lot of information, but you want something custom. You want to decide if a new design for your site will better engage your users. In other words, you want to A/B test a design and make a decision based on the data.
+
+![A/B test example](../../../images/ab-test/ab-test.png)
+
+For this task, you could create a New Relic One application, using React, the New Relic One software development kit, and the limitless power of modern web technologies.
+
+New Relic One applications are built with one of web development's most popular JavaScript libraries: React. Because you have freedom when writing React code, you can customize your app logic, design your own components, or take advantage of the abundance of open source component libraries. So, for your A/B test app, if you want to write custom logic to end the test based on results, you can do so.
+
+The New Relic One software development kit (SDK) allows you to create, serve, publish, and deploy applications to New Relic One. It also provides a host of React components for gathering data, presenting information, handling user interactions, and more. You use components like `Button` and `Dropdown` to create an interactive experience that looks and feels native to New Relic. You use `Table` and `Chart` components to display data from your New Relic account or elsewhere. When building your A/B test application, you'd use the SDK's `NrqlQuery` component to fetch Browser data from your account.
+
+With custom React code, SDK components, and the wide world of open source libraries, you can create your A/B test application in New Relic One. But before you create one for yourself, you might want to check the app catalog to see if someone has beaten you to it! If the catalog already had an app for that, you could add it to your account with a couple clicks, another benefit of creating apps in New Relic One.
+
+Throughout this course, you’re going to build a real-world New Relic One application for running and managing A/B tests. You’ll visualize Browser data for your competing designs, see historical data from past tests, and even choose a winning design and end the test, all from your New Relic One application! But before you get into the weeds of building charts and making http requests, you need to learn what New Relic One applications are made of.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the first lesson: [_Spin up your demo services_](../build-apps/ab-test/demo-setup).
+
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/install-sdk.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/install-sdk.mdx
new file mode 100644
index 000000000..062937002
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/install-sdk.mdx
@@ -0,0 +1,124 @@
+---
+path: '/build-apps/ab-test/install-nr1'
+title: 'Install and configure the New Relic One CLI'
+template: 'GuideTemplate'
+description: 'Install and configure the New Relic One CLI'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Spin up your demo services_](../demo-setup), before starting this one.
+
+
+
+One of the primary elements of the New Relic One SDK is the command line interface (CLI). To create a Nerdpack , you'll need to install the SDK, configure the CLI to work with your New Relic account, and then utilize its `create` command.
+
+# Install and configure the CLI
+
+
+
+
+
+**Select or create a User key.**
+
+From the top navigation, go to **API keys**:
+
+![API keys](../../../images/ab-test/api-keys.png)
+
+If you have an existing User key, copy it:
+
+![Copy existing key](../../../images/ab-test/copy-existing-key.png)
+
+If not, click **Create key**:
+
+![Create key button](../../../images/ab-test/create-key-button.png)
+
+Select **User** for **Key type**, optionally add a name and notes, and click **Create key**:
+
+![Create key details](../../../images/ab-test/create-key-details.png)
+
+You use this key to associate your New Relic account with the CLI. The CLI then uses the key to manage entities within your account.
+
+
+
+
+
+**Download the SDK's installer for your operating system.**
+
+Install the SDK for your operating system:
+
+- [Mac download](https://cli.nr-ext.net/installer.pkg)
+- [Linux download](https://cli.nr-ext.net/installer.sh)
+- [Windows download](https://cli.nr-ext.net/installer.exe)
+
+Once you've installed the SDK, you'll have access to the `nr1` CLI. Verify this by checking your SDK version:
+
+```sh
+nr1 --version
+```
+
+
+
+It’s important to distinguish between the `newrelic` CLI and the `nr1` CLI. `newrelic` is for managing entities in your New Relic account. `nr1` is for managing New Relic One applications.
+
+
+
+
+
+
+
+**Associate your New Relic account as a CLI profile.**
+
+Use `nr1 profiles:add` to associate the CLI with your New Relic account:
+
+```sh
+nr1 profiles:add --name --api-key --region
+```
+
+
+
+Make sure to replace the ``, the ``, and the region (either `us` or `eu`) with real values.
+
+
+
+Profiles let you select which New Relic account you want to run commands against. If you have multiple accounts, you can view them with `profiles:list`:
+
+```sh
+nr1 profiles:list
+```
+
+Notice that one profile is your default profile. This is the account your commands will run against, unless you specify another. To specify a profile for a particular command, use the `--profile` option:
+
+```sh
+nr1 create --profile
+```
+
+If this is your first time using the CLI, then the profile you just added is your default profile. If you have other profiles, you need to set your default to the one you'd like to use for this course:
+
+```sh
+nr1 profiles:default
+```
+
+
+
+If you forget these commands, you can look them up in the profiles help menu:
+
+```sh
+nr1 profiles --help
+```
+
+
+
+
+
+
+
+Now, you’re ready to build an application with the New Relic One CLI.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Create a Nerdpack_](../create-nerdpack).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/navigation.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/navigation.mdx
new file mode 100644
index 000000000..543cd3ebe
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/navigation.mdx
@@ -0,0 +1,385 @@
+---
+path: '/build-apps/ab-test/navigation'
+title: 'Add navigation to your nerdlet'
+template: 'GuideTemplate'
+description: 'Add navigation to your nerdlet'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add PlatformStateContext to your nerdlet_](../platform-state-context), before starting this one.
+
+
+
+In the last lesson, you used `PlatformStateContext` from the New Relic One SDK to lookup the application user's account ID and the time range they selected from the app's time picker. Now, you'll learn about another component that interacts with the New Relic platform: `navigation`.
+
+The `navigation` component lets you open entities, navigate to entities, and build Location objects for entities from your nerdlet. You can also use `navigation` for other nerdlets and launchers.
+
+
+
+
+
+Change to the _add-navigation_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-navigation
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In _newsletter-signups.js_, create a new method, called `NewsletterSignups.openApmEntity()`:
+
+```js fileName=nerdlets/ab-test-nerdlet/newsletter-signups.js lineHighlight=7,11-13
+import React from 'react';
+import {
+ HeadingText,
+ LineChart,
+ NrqlQuery,
+ PlatformStateContext,
+ navigation,
+} from 'nr1';
+
+export default class NewsletterSignups extends React.Component {
+ openAPMEntity() {
+ navigation.openStackedEntity(ENTITY_GUID)
+ }
+
+ render() {
+ return
+ }
+}
+```
+
+You'll use `Stack` and `StackItem` to lay out the button on the far right side of the same row that the `HeadingText` is on.
+
+
+
+
+
+Lay out the `Stack`:
+
+```js fileName=nerdlets/ab-test-nerdlet/newsletter-signups.js lineHighlight=22-33
+import React from 'react';
+import {
+ Button,
+ HeadingText,
+ LineChart,
+ NrqlQuery,
+ PlatformStateContext,
+ Stack,
+ StackItem,
+ navigation,
+} from 'nr1';
+
+const ENTITY_GUID = ""
+
+export default class NewsletterSignups extends React.Component {
+ openAPMEntity() {
+ navigation.openStackedEntity(ENTITY_GUID)
+ }
+
+ render() {
+ return
+ }
+}
+```
+
+Here, you used a full-width `Stack` to set up the layout for the row. To make the `HeadingText` fill the entire row but the width of the button, you used the `StackItem.grow` prop.
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/add-navigation/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account. It also allows your app to make Nerdgraph requests on behalf of your account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+[View your application](https://one.newrelic.com?nerdpacks=local):
+
+![Your New Relic One application with an App performance button](../../../images/ab-test/performance-button.png)
+
+Click **App performance**:
+
+![APM stacked entity](../../../images/ab-test/stacked-entity.png)
+
+Now you see the stacked entity!
+
+
+
+
+
+Congratulations! You're finished writing all the code you'll write for you New Relic One A/B test application. Now, you have an application reporting New Relic data from your demo application that is running an A/B test. You've created several charts, buttons, and other UI elements. You've organized your components into a readable and usable view.
+
+On top of the visuals, you've supplied data to your charts from multiple data sources in and out of New Relic. You've created some backend functionality which utilizes your New Relic One application's own data store. You've also utilized the platform APIs for interacting with platform UI and showing a stacked entity view.
+
+You've really accomplished a lot throughout this course, so far. There are only a few things left to do! First, is to learn how to deploy and subscribe to your New Relic application so that it can run on our platform instead of your own local server. Second, is to learn how to deal with some common issues you might see in New Relic One application development.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Describe your app for the catalog_](../catalog).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/nerdstorage.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/nerdstorage.mdx
new file mode 100644
index 000000000..fad943803
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/nerdstorage.mdx
@@ -0,0 +1,754 @@
+---
+path: '/build-apps/ab-test/nerdstorage'
+title: 'Access NerdStorage from your nerdlet'
+template: 'GuideTemplate'
+description: 'Access NerdStorage from your nerdlet'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Customize NRQL data_](../nrql-customizations), before starting this one.
+
+
+
+
+In previous lessons, you created a user experience in your application which includes charts and a button to end your A/B test. You also configured your charts to query real data from New Relic's database. However, you left mock data in two charts:
+
+- **Past tests**
+- **Total cancellations per version**
+
+Both of these charts hold data that you can't get from New Relic. **Past tests** shows historical test information, whereas **Total cancellations per version** shows the number of cancellations each version sees over time. Focus, first on **Past tests**.
+
+**Past tests** shows historical test information, so you need to store information about each test upon completion. And you have all the tools you need to accomplish this task. First, you have an **End test** button that triggers the save action. Second, you know how to use NerdGraph and NerdStorage to hold the test information.
+
+## Save test information to NerdStorage
+
+
+
+
+
+Change to the _add-nerdstorage_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-nerdstorage
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet/end-test.js_, create a method, `EndTestButton.endTest()`:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=3,37,48-70
+import React from 'react';
+import {
+ AccountStorageMutation,
+ BlockText,
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Modal,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ modalHidden: true,
+ }
+
+ this.showModal = this.showModal.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ this.endTest = this.endTest.bind(this);
+ }
+
+ closeModal() {
+ this.setState({ modalHidden: true });
+ }
+
+ showModal() {
+ this.setState({ modalHidden: false });
+ }
+
+ endTest() {
+ const today = new Date();
+ const dd = String(today.getDate()).padStart(2, '0');
+ const mm = String(today.getMonth() + 1).padStart(2, '0');
+ const yyyy = today.getFullYear();
+ const endDate = `${mm}-${dd}-${yyyy}`
+
+ AccountStorageMutation.mutate(
+ {
+ accountId: this.props.accountId,
+ actionType: AccountStorageMutation.ACTION_TYPE.WRITE_DOCUMENT,
+ collection: "past-tests",
+ documentId: endDate,
+ document: {
+ versionADescription: this.props.versionADescription,
+ versionBDescription: this.props.versionBDescription,
+ winner: this.props.selectedVersion,
+ }
+ }
+ )
+
+ this.closeModal();
+ }
+
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version {this.props.selectedVersion}
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+With this code, you build a formatted date string, `endDate`. Then, you mutate account storage with a call to `AccountStorageMutation.mutate()`. You pass your account identifier, the `WRITE_DOCUMENT` action type, "past-tests" as the collection name, `endDate` as the `documentId`, and the document data, which includes the version descriptions and the winner.
+
+You pass `WRITE_DOCUMENT` because you're either creating a new document or updating a document, if one with a matching collection and `documentId` already exists. `documentId` is `endDate`, which is helpful for only creating one record per day.
+
+
+
+If you suspected you might complete multiple tests in one day, you'd need to change this logic.
+
+
+
+
+
+
+
+Notice `this.props.closeModal()` at the end of the method. This closes the modal that shows when you try to end a test. Therefore, you can replace the `onClick` callback for your **Yes, end test** button in `EndTestButton.render()`:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=87
+import React from 'react';
+import {
+ AccountStorageMutation,
+ BlockText,
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Modal,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ modalHidden: true,
+ }
+
+ this.showModal = this.showModal.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ this.endTest = this.endTest.bind(this);
+ }
+
+ closeModal() {
+ this.setState({ modalHidden: true });
+ }
+
+ showModal() {
+ this.setState({ modalHidden: false });
+ }
+
+ endTest() {
+ const today = new Date();
+ const dd = String(today.getDate()).padStart(2, '0');
+ const mm = String(today.getMonth() + 1).padStart(2, '0');
+ const yyyy = today.getFullYear();
+ const endDate = `${mm}-${dd}-${yyyy}`
+
+ AccountStorageMutation.mutate(
+ {
+ accountId: this.props.accountId,
+ actionType: AccountStorageMutation.ACTION_TYPE.WRITE_DOCUMENT,
+ collection: "past-tests",
+ documentId: endDate,
+ document: {
+ versionADescription: this.props.versionADescription,
+ versionBDescription: this.props.versionBDescription,
+ winner: this.props.selectedVersion,
+ }
+ }
+ )
+
+ this.closeModal();
+ }
+
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version {this.props.selectedVersion}
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+When you end the test, `endTest` stores your test data in account storage and closes the modal. This is only half the behavior you need to populate your table, you also need to query this collection from account storage.
+
+
+
+
+
+In your Nerdlet's _index.js_ file, pass the account ID and version descriptions to `EndTestSection`:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js
+import React from 'react';
+import { ChartGroup, Grid, GridItem } from 'nr1';
+import EndTestSection from './end-test';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionDescription from './description';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+const ACCOUNT_ID = 123456 //
+const VERSION_A_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter"'
+const VERSION_B_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter and get a free shirt!"'
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+
+
+
+ Are you sure?
+
+ If you end the test, all your users will receive the version you selected:
+
+
+
+ Version {this.props.selectedVersion}
+
+
+
+
+
+
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+
+ End test
+
+
+
+ }
+}
+```
+
+
+
+
+
+## Fetch test information from NerdStorage
+
+
+
+
+
+In _past-tests.js_, update `PastTests` to fetch data from account storage and display it in the `TableChart`:
+
+```js fileName=nerdlets/ab-test-nerdlet/past-tests.js lineHighlight=2-7,17,24-46
+import React from 'react';
+import {
+ AccountStorageQuery,
+ HeadingText,
+ Spinner,
+ TableChart,
+} from 'nr1';
+
+export default class PastTests extends React.Component {
+ render() {
+ const historicalData = {
+ metadata: {
+ id: 'past-tests',
+ name: 'Past tests',
+ columns: ['endDate', 'versionADescription', 'versionBDescription', 'winner'],
+ },
+ data: [],
+ }
+
+ return
+
+ Past tests
+
+
+ {({ loading, error, data }) => {
+ if (loading) {
+ return ;
+ }
+ if (error) {
+ console.debug(error);
+ return 'There was an error fetching your data.';
+ }
+ data.forEach(
+ function (currentValue, index) {
+ this[index] = {
+ endDate: currentValue.id,
+ versionADescription: currentValue.document.versionADescription,
+ versionBDescription: currentValue.document.versionBDescription,
+ winner: currentValue.document.winner,
+ }
+ }, data
+ )
+ historicalData.data = data
+ return
+ }}
+
+
+ }
+}
+```
+
+First, you removed the mocked data from `historicalData`. Then, you used `AccountStorageQuery` to fetch data from NerdStorage. You passed your account identifier and the name of the collection in which you stored the document.
+
+Notice that there are three return values from the query:
+
+- `loading`
+- `error`
+- `data`
+
+If the query is in progress, `loading` is `true`. In this case, you show a `Spinner` user interface component to let users know that the table isn't yet ready. If the query returned an error, you can see information about that error in the `error` variable. You log that information to the console for debugging purposes and return an error message. If the query returned successfully, you can access the data in the `data` variable. Because the data doesn't match the format that the `TableChart` requires, you loop through the data, format it according to the `TableChart` specifications, and set it on `historicalData.data`. Finally, you pass `historicalData` to your `TableChart`.
+
+
+
+
+
+In your Nerdlet's _index.js_ file, pass your `ACCOUNT_ID` to `PastTests`:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=57-59
+import React from 'react';
+import { ChartGroup, Grid, GridItem } from 'nr1';
+import EndTestSection from './end-test';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionDescription from './description';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+const ACCOUNT_ID = 3014918 //
+const VERSION_A_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter"'
+const VERSION_B_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter and get a free shirt!"'
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+ }
+}
+```
+
+
+
+Make sure you replace `` with your actual New Relic [account id](https://docs.newrelic.com/docs/accounts/accounts-billing/account-setup/account-id).
+
+
+
+
+
+
+
+Navigate to the root of your Nerdpack at `nru-programmability-course/add-nerdstorage/ab-test`.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account. It also allows your app to make Nerdgraph requests on behalf of your account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+[View your changes](https://one.newrelic.com?nerdpacks=local). At first, you should see no data:
+
+![Past tests before you end the test](../../../images/ab-test/past-tests-before.png)
+
+
+Click **End test** and approve your action in the modal. Now, you see data in **Past tests**:
+
+![Past tests after you end the test](../../../images/ab-test/past-tests-after.png)
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+Great work! You now have experience using the NerdStorage query and mutation components. However, there's still one more chart with mocked data: **Total cancellations per version**.
+
+**Total cancellations per version** is different from the other charts in your A/B test application. Because New Relic knows nothing about how many users unsubscribe to your newsletter, you need to request that information from an external source. For the purposes of this course, we've created an external data service that provides fake newsletter cancellation data for you to consume in your application. This mock service lives at https://api.nerdsletter.net/cancellations.
+
+New Relic One applications are React applications, which means that you can use React and Javascript facilities to gather data not only from New Relic, but any external service. In a later lesson, you'll learn how to look beyond New Relic for data that will inform you of how your software is helping you achieve your business objectives. But before you learn that, you need to know something about our mock data service: it requires an Authorization header!
+
+In this lesson, you learned how to use NerdStorage to query and mutate data in your application's own data store. While NerdStorage is a great place for many categories of data, it's not appropriate for sensitive data, like a token you would pass in an Authorization header to an external service. For that, you'd use NerdStorageVault.
+
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Access NerdStorageVault from your nerdlet_](../nerdstoragevault).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/nerdstoragevault.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/nerdstoragevault.mdx
new file mode 100644
index 000000000..5b4c36da5
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/nerdstoragevault.mdx
@@ -0,0 +1,1276 @@
+---
+path: '/build-apps/ab-test/nerdstoragevault'
+title: 'Access NerdStorageVault from your nerdlet'
+template: 'GuideTemplate'
+description: 'Access NerdStorageVault from your nerdlet'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Access NerdStorage from your nerdlet_](../nerdstorage), before starting this one.
+
+
+
+
+Throughout this course, you're building a New Relic One application that gathers telemetry data from a demo web service that's running an A/B test on a newsletter signup form. The purpose of your New Relic One application is to help you understand how design changes impact the number of high quality newsletter subscriptions your service gets. The business objective, to increase your service's high quality newsletter subscriptions, relies primarily on three key pieces of information:
+
+- Number of page views per version
+- Number of subscriptions per version
+- Number of cancellations
+
+Cancellations are important because if one design version of your newsletter signup form results in a high number of subscriptions but also a high number of cancellations, then those subscriptions are not as valuable.
+
+In previous lessons, you gathered data for page views and subscriptions from New Relic's database (NRDB), but you still need cancellation data. Your demo application does not report cancellation data to New Relic, so you need to fetch that data from an external source. We've provided a service at https://api.nerdsletter.net/cancellations to return fake cancellation data for the purposes of this course. If you visit this URL in your browser, you'll see a brief message: "Unauthorized". This is because we created this service with a requirement that whoever requests its data must pass an Authorization header with the bearer token `ABC123`.
+
+So before you can request cancellation data from api.nerdsletter.net, you need to implement a few new behaviors in your application:
+
+- Provide a mechanism for inputting an authorization token
+- Persist the authorization token in a secure data store
+
+To input your authorization token, you'll use a `Modal` with a `TextField`. The secure data store you'll use is called `NerdStorageVault`. It's different from `NerdStorage`, which you used in the last lesson, because it only supports user storage and encrypts its data.
+
+## Store your API token
+
+
+
+
+
+Change to the _add-nerdstoragevault_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-nerdstoragevault
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In your Nerdlet's _index.js_ file, initialize `state` in `AbTestNerdletNerdlet` with a `null` token default:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=17-23
+import React from 'react';
+import { ChartGroup, Grid, GridItem } from 'nr1';
+import EndTestSection from './end-test';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionDescription from './description';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+const ACCOUNT_ID = 123456 //
+const VERSION_A_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter"'
+const VERSION_B_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter and get a free shirt!"'
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ token: null,
+ }
+ }
+
+ render() {
+ return
+ }
+}
+```
+
+When you call `storeToken()` with a new token value, your nerdlet uses `NerdGraph` APIs to mutate `NerdStorageVault` data for the `api_token` key. If the request to `NerdGraph` is successful, `storeToken()` updates `state.token` so that the new token is locally accessible.
+
+Unlike `NerdStorage`, which has query and mutation components for your convenience, `NerdStorageVault` has no components in the SDK. Instead, you have to use `NerdGraphQuery` and `NerdGraphMutation` to interact with it
+
+
+
+It's important to remember that `NerdStorageVault` is limited to the user scope. Any other users of your New Relic One application will have their own `NerdStorageVault` data. This means that even other users on your account will need to enter their token separately.
+
+
+
+
+
+
+
+## Query your API token
+
+
+
+
+
+First, create methods and `state` to show and hide your API token prompt:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=26,31-32,65-71
+import React from 'react';
+import {
+ ChartGroup,
+ Grid,
+ GridItem,
+ NerdGraphMutation,
+} from 'nr1';
+import EndTestSection from './end-test';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionDescription from './description';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+const ACCOUNT_ID = 123456 //
+const VERSION_A_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter"'
+const VERSION_B_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter and get a free shirt!"'
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ hideTokenPrompt: true,
+ token: null,
+ }
+
+ this.storeToken = this.storeToken.bind(this);
+ this.showPrompt = this.showPrompt.bind(this);
+ this.hidePrompt = this.hidePrompt.bind(this);
+ }
+
+ storeToken(newToken) {
+ if (newToken != this.state.token) {
+ const mutation = `
+ mutation($key: String!, $token: SecureValue!) {
+ nerdStorageVaultWriteSecret(
+ scope: { actor: CURRENT_USER }
+ secret: { key: $key, value: $token }
+ ) {
+ status
+ errors {
+ message
+ type
+ }
+ }
+ }
+ `;
+ const variables = {
+ key: "api_token",
+ token: newToken,
+ };
+ NerdGraphMutation.mutate({ mutation: mutation, variables: variables }).then(
+ (data) => {
+ if (data.data.nerdStorageVaultWriteSecret.status === "SUCCESS") {
+ this.setState({token: newToken})
+ }
+ }
+ );
+ }
+ }
+
+ showPrompt() {
+ this.setState({ hideTokenPrompt: false });
+ }
+
+ hidePrompt() {
+ this.setState({ hideTokenPrompt: true });
+ }
+
+ render() {
+ return
+ }
+}
+```
+
+Here, you've implemented another React lifecycle method, called `.componentDidUpdate()`. Now, every time your nerdlet's `state.token` changes, `TotalCancellations` gets a new token prop, which triggers `.componentDidUpdate()`. In `.componentDidUpdate()`, you check that the incoming token is not the same as the last token it knew about, which is stored in local state. If the incoming token is different, you log a message with the new token and update `state.lastToken`.
+
+This logic prepares your code for a future change to use your API token in a request to a third-party service.
+
+
+
+
+
+With your nerdpack served locally, [view your application](https://one.newrelic.com?nerdpacks=local) to see the log from `TotalCancellations` in your browser's console:
+
+![Total cancellations request log](../../../images/ab-test/request-log.png)
+
+If you change your token, you'll see another log from `TotalCancellations` with your updated token.
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+Now you know how to use `NerdGraphQuery` and `NerdGraphMutation` to manage data in `NerdStorageVault`! Remember, use `NerdStorage` for your New Relic One application's non-sensitive data and `NerdStorageVault` for the sensitive stuff like tokens, passwords, and other secrets. As a bonus, you created a way to manage your token in `NerdStorageVault` from the user interface. You also passed the token to your `TotalCancellations` component for later use.
+
+Whether it's been with `NrqlQuery`, `AccountStorageQuery`, `AccountStorageMutation`, `NerdGraphQuery`, or `NerdGraphMutation`, you've learned several ways to interact with New Relic data in your New Relic One application. But New Relic One applications aren't just another way to show New Relic data. The purpose of New Relic One applications is to show you how your software is helping you meet your business objectives. Sometimes, New Relic data is all you need to make that happen, but other times you need to look beyond New Relic for data to fill in the gaps.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Fetch data from a third-party service_](../third-party-service).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/nrql-customizations.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/nrql-customizations.mdx
new file mode 100644
index 000000000..e47f169af
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/nrql-customizations.mdx
@@ -0,0 +1,474 @@
+---
+path: '/build-apps/ab-test/nrql-customizations'
+title: 'Customize NRQL data'
+template: 'GuideTemplate'
+description: 'Customize NRQL data'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add NrqlQuery components to your nerdlet_](../nrql), before starting this one.
+
+
+
+In this series, you're creating a New Relic One application, or NR1 app for short, that shows A/B test data in a variety of charts. Most of the charts need to show data that comes from a demo service you spun up at the beginning of this series. To refresh your memory on what you're building, review your design guide:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+In the last lesson, you supplied real data, which comes from New Relic's database, to the following charts:
+
+- **Newsletter subscriptions per version**
+- **Version A - Page views**
+- **Version B - Page views**
+- **Total subscriptions per version**
+
+Because you have experience querying subscription and page view data, you may feel ready to supply data to even more charts:
+
+- **Version A - Page views vs. subscriptions**
+- **Version B - Page views vs. subscriptions**
+
+However, these charts are different than the charts you've supplied data to because they compare data from two different sources: `subscription` events and `pageView` events.
+
+In this lesson, you learn how to format data from multiple sources to exist in the same chart.
+
+## Experiment with data in the Data explorer
+
+
+
+
+
+From your [New Relic](https://one.newrelic.com) homepage's top navigation menu, go to to the **Data explorer**:
+
+![Data explorer icon](../../../images/ab-test/nav-to-data-explorer.png)
+
+Here, you can experiment with `subscription` event and `pageView` event queries to make sure you fetch the right data in your app.
+
+
+
+
+
+Select **subscription** from the **Custom events** menu:
+
+![Subscription events in the data explorer](../../../images/ab-test/data-explorer-subscription.png)
+
+This queries NRDB for subscription event totals per minute over the last 30 minutes and shows that data in a chart:
+
+![Subscription events over the last 30 minutes](../../../images/ab-test/data-explorer-subscription-count.png)
+
+
+
+
+
+Click **Dimensions** to see a list of the attributes associated with these subscription events:
+
+![Subscription event dimensions](../../../images/ab-test/data-explorer-subscription-dimensions.png)
+
+You can filter and group subscription events using these dimensions. For **Version A - Page views vs. subscriptions** and **Version B - Page views vs. subscriptions**, you want to filter your subscription totals by `page_version`.
+
+
+
+
+
+Click the **NRQL** query to navigate to the Query builder:
+
+![Subscription event dimensions](../../../images/ab-test/data-explorer-subscription-edit-nrql.png)
+
+Here, you can view and manually edit the query to fetch the data you need.
+
+
+
+
+
+Add a `WHERE` clause to filter subscription totals by `page_version`:
+
+```sql
+SELECT count(*) FROM subscription WHERE page_version = 'a' SINCE 30 MINUTES AGO TIMESERIES
+```
+
+ Remove the `TIMESERIES` clause to fetch a count:
+
+ ```sql
+ SELECT count(*) FROM subscription WHERE page_version = 'a' SINCE 30 MINUTES AGO
+ ```
+
+ Modify the `SINCE` clause to see totals over the past week:
+
+ ```sql
+ SELECT count(*) FROM subscription WHERE page_version = 'a' SINCE 7 DAYS AGO
+ ```
+
+Click **Run** and see the data visualized in a billboard chart instead of a line chart:
+
+![NRQL query for total subscriptions from version A](../../../images/ab-test/subscription-total-billboard.png)
+
+
+
+
+
+**Version A - Page views vs. subscriptions** and **Version B - Page views vs. subscriptions** need a combined total of four data values:
+
+- All-time subscription totals for version A
+- All-time page view totals for version A
+- All-time subscription totals for version B
+- All-time page view totals for version B
+
+Experiment with the Query builder to discover the four queries which pull the right data. In the end, you'll come up with the following four queries:
+
+```sql
+SELECT count(*) FROM subscription WHERE page_version = 'a' SINCE 7 DAYS AGO
+SELECT count(*) FROM pageView WHERE page_version = 'a' SINCE 7 DAYS AGO
+SELECT count(*) FROM subscription WHERE page_version = 'b' SINCE 7 DAYS AGO
+SELECT count(*) FROM pageView WHERE page_version = 'b' SINCE 7 DAYS AGO
+```
+
+
+
+Unlike other structured query languages, NRQL doesn't provide a mechanism for joining data across sources. This is why you have to perform two queries to get `subscription` event totals and `pageView` event totals.
+
+
+
+Until now, you've provided every chart with a single query. Here, you have to provide two queries per chart. In the remaining steps, you'll learn how to customize `NrqlQuery` results to merge data from multiple sources.
+
+
+
+
+
+## Merge NRQL from multiple sources
+
+
+
+
+
+Change to the _customize-nrql-data_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/customize-nrql-data
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In your Nerdlet's _totals.js_ file, implement `constructor()` and the React lifecycle method `componentDidMount()` in `VersionTotals`:
+
+```js fileName=nerdlets/ab-test-nerdlet/totals.js
+import React from 'react';
+import {
+ HeadingText,
+ NrqlQuery,
+ TableChart,
+} from 'nr1';
+
+export default class VersionTotals extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ tableData: {
+ metadata: {
+ id: `totals-${this.props.version}`,
+ name: `Version ${this.props.version}`,
+ columns: ['name', 'count'],
+ },
+ data: [
+ {
+ name: 'Subscriptions',
+ count: 0
+ },
+ {
+ name: 'Page views',
+ count: 0
+ },
+ ],
+ }
+ }
+ }
+
+ componentDidMount() {
+ NrqlQuery.query({
+ accountId: this.props.accountId,
+ query: `SELECT count(*) FROM subscription WHERE page_version = '${this.props.version}' SINCE 7 DAYS AGO`,
+ formatType: NrqlQuery.FORMAT_TYPE.RAW
+ }).then(({ data }) => {
+ if (data.raw) {
+ let tableData = {...this.state.tableData}
+ tableData.data[0].count = data.raw.results[0].count
+ this.setState({tableData})
+ }
+ })
+
+ NrqlQuery.query({
+ accountId: this.props.accountId,
+ query: `SELECT count(*) FROM pageView WHERE page_version = '${this.props.version}' SINCE 7 DAYS AGO`,
+ formatType: NrqlQuery.FORMAT_TYPE.RAW
+ }).then(({ data }) => {
+ if (data.raw) {
+ let tableData = {...this.state.tableData}
+ tableData.data[1].count = data.raw.results[0].count
+ this.setState({tableData})
+ }
+
+ })
+ }
+
+ render() {
+ return
+
+ Version {this.props.version.toUpperCase()} - Page views vs. subscriptions
+
+
+
+ }
+}
+```
+
+
+
+Make sure you replace `` with your actual New Relic [account id](https://docs.newrelic.com/docs/accounts/accounts-billing/account-setup/account-id).
+
+
+
+Here, you initialize your state in the component's constructor. The state has a single value, `tableData`, which holds series data and metadata for the `TableChart`. In [`componentDidMount()`](https://reactjs.org/docs/react-component.html#componentdidmount), you query the data that populates the data in this state.
+
+ `componentDidMount()` is a React lifecycle method that is called when a component is mounted in the component tree. You use this method, instead of the constructor, to query data because your logic, which requests data from New Relic, introduces side effects and sets state values, neither of which you should do in the constructor.
+
+ In `componentDidMount()`, you use `NrqlQuery` in a new way. First, you specified the `formatType` to `NrqlQuery.FORMAT_TYPE.RAW` because you're going to modify the data instead of supplying it directly to a chart Before, you used its JSX form in the `render()` method of a chart. Second, you called its `query()` method and handled the results in a `then()` callback, which logs them to the console.
+
+
+
+
+
+In your Nerdlet's _index.js_ file, create a constant called `ACCOUNT_ID` and pass it to `VersionTotals`:
+
+```js fileName=nerdlets/ab-test-nerdlet/ lineHighlight=12,36-41
+import React from 'react';
+import { ChartGroup, Grid, GridItem } from 'nr1';
+import EndTestSection from './end-test';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionDescription from './description';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+const ACCOUNT_ID = 123456 //
+const VERSION_A_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter"'
+const VERSION_B_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter and get a free shirt!"'
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+ }
+}
+```
+
+
+
+Make sure you replace `` with your actual New Relic [account id](https://docs.newrelic.com/docs/accounts/accounts-billing/account-setup/account-id).
+
+
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/customize-nrql-data/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+Navigate to your app in [New Relic](https://one.newrelic.com?nerdpacks=local).
+
+
+
+
+
+To view your logs, which are useful for uncovering errors in your application, turn on developer tools in your favorite browser and navigate to the **Console**:
+
+- [FireFox instructions](https://developer.mozilla.org/en-US/docs/Tools/Web_Console)
+- [Safari instructions](https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/Web_Inspector_Tutorial/DebuggingyourWebpage/DebuggingyourWebpage.html#//apple_ref/doc/uid/TP40017576-CH5-DontLinkElementID_24)
+- [Chrome instructions](https://developers.google.com/web/tools/chrome-devtools#console)
+
+With your app on-screen, notice the NRQL data in the console:
+
+![Your query data in the console](../../../images/ab-test/raw-data-in-console.png)
+
+This console log tells you that you can find the data you're looking for, namely the `subscription` or `pageView` count, at `data.raw.results[0].count`. The next step is to store that count in `state.tableData`.
+
+
+
+
+
+In _totals.js_, store the query data in state:
+
+```js fileName=nerdlets/ab-test-nerdlet/totals.js lineHighlight=40-42,52-54
+import React from 'react';
+import {
+ HeadingText,
+ NrqlQuery,
+ TableChart,
+} from 'nr1';
+
+export default class VersionTotals extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ tableData: {
+ metadata: {
+ id: `totals-${this.props.version}`,
+ name: `Version ${this.props.version}`,
+ columns: ['name', 'count'],
+ },
+ data: [
+ {
+ name: 'Subscriptions',
+ count: 0
+ },
+ {
+ name: 'Page views',
+ count: 0
+ },
+ ],
+ }
+ }
+ }
+
+ componentDidMount() {
+ NrqlQuery.query({
+ accountId: this.props.accountId,
+ query: `SELECT count(*) FROM subscription WHERE page_version = '${this.props.version}' SINCE 7 DAYS AGO`,
+ formatType: NrqlQuery.FORMAT_TYPE.RAW
+ }).then(({ data }) => {
+ if (data.raw) {
+ let tableData = {...this.state.tableData}
+ tableData.data[0].count = data.raw.results[0].count
+ this.setState({tableData})
+ }
+ })
+
+ NrqlQuery.query({
+ accountId: this.props.accountId,
+ query: `SELECT count(*) FROM pageView WHERE page_version = '${this.props.version}' SINCE 7 DAYS AGO`,
+ formatType: NrqlQuery.FORMAT_TYPE.RAW
+ }).then(({ data }) => {
+ if (data.raw) {
+ let tableData = {...this.state.tableData}
+ tableData.data[1].count = data.raw.results[0].count
+ this.setState({tableData})
+ }
+
+ })
+ }
+
+ render() {
+ return
+
+ Version {this.props.version.toUpperCase()} - Page views vs. subscriptions
+
+
+
+ }
+}
+```
+
+Pay close attention to the data you're accessing. The array indices are particularly important to get right. The subscription data should go to `state.tableData.data[0].count` because in `tableData`, it is the first element in the data array:
+
+```json
+data: [
+ {
+ name: 'Subscriptions',
+ count: 0
+ },
+ {
+ name: 'Page views',
+ count: 0
+ },
+],
+```
+
+By similar logic, page view data should go to `state.tableData.data[1].count`.
+
+
+
+
+
+With your nerdpack served locally, [view your application](https://one.newrelic.com?nerdpacks=local) to see your charts serving real data:
+
+![Your New Relic One application showing real subscription and page view data](../../../images/ab-test/real-table-chart-data.png)
+
+
+
+
+
+In this lesson, you learned how to use `NrqlQuery.query()` to fetch data from multiple sources and stitch them together into data your chart can use. Notice that there are still two charts in your NR1 application that use mock data:
+
+- **Total unsubscriptions per version**
+- **Past tests**
+
+Unfortunately, your demo application doesn't create custom New Relic events when a user unsubscribes from your newsletter or you end an A/B test. In the next lesson, you'll learn how to use NerdGraph and NerdStorage to populate **Past tests**.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Access NerdStorage from your nerdlet_](../nerdstorage).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/nrql.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/nrql.mdx
new file mode 100644
index 000000000..3e4ddf960
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/nrql.mdx
@@ -0,0 +1,445 @@
+---
+path: '/build-apps/ab-test/nrql'
+title: 'Add NrqlQuery components to your nerdlet'
+template: 'GuideTemplate'
+description: 'Add NrqlQuery components to your nerdlet'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Present an end test confirmation modal_](../confirmation-modal), before starting this one.
+
+
+
+In this series, you're building a full-fledged New Relic One application that ingests data from a demo service that's running an A/B test. The app shows the A/B test data in interesting ways so that you'll ultimately be able to choose whether Version A or Version B is more effective at accomplishing your business goal: to increase high-quality newsletter subscriptions.
+
+In past tutorials, you created charts to visualize your data, and you organized those charts so you can use and understand them. You also created a form in your app for ending your test once you're confident in the most effective version. Until now, though, you can't gauge which version is more effective because your charts have been showing mock data, such as:
+
+```js
+const versionASignups = {
+ metadata: {
+ id: 'version-a-newsletter-signups',
+ name: 'Version A',
+ viz: 'main',
+ color: 'blue',
+ },
+ data: [
+ { x: 0, y: 0 },
+ { x: 10, y: 10 },
+ { x: 20, y: 15 },
+ { x: 30, y: 5 },
+ { x: 40, y: 30 },
+ { x: 50, y: 25 },
+ ],
+}
+```
+
+A chart's `data` prop is useful for supplying manually-crafted data like this or even reformatted third-party data. But for many of your charts, you want to show real-time New Relic data. For example, **Newsletter subscriptions by version** should show subscription data, which exists in New Relic's database, NRDB for short.
+
+To query NRDB, you first need to know what data you're looking for. Remember your [demo backend service](../demo-setup)? Well, that service reports a subscription event to New Relic when a user subscribes to a newsletter from your site. You can view these subscription events in New Relic while your demo services are running.
+
+## View Subscription Events in New Relic
+
+Before you query NRDB from your charts, experiment with querying data from New Relic's web interface.
+
+
+
+
+
+From your [New Relic](https://one.newrelic.com) homepage, go to to the **Data explorer** from the top navigation menu:
+
+![Data explorer icon](../../../images/ab-test/nav-to-data-explorer.png)
+
+[Data explorer](https://docs.newrelic.com/docs/insights/use-insights-ui/explore-data/data-explorer-query-chart-event-data) lets you explore your telemetry data:
+
+- metrics
+- events
+- logs
+- traces
+
+Since the backend submits newsletter subscriptions as events to New Relic, you can see them in this view.
+
+
+
+
+
+Select **subscription** from the **Custom events** menu:
+
+![Subscription events in the data explorer](../../../images/ab-test/data-explorer-subscription.png)
+
+This queries NRDB for subscription event totals per minute over the last 30 minutes and shows that data in a chart:
+
+![Subscription events over the last 30 minutes](../../../images/ab-test/data-explorer-subscription-count.png)
+
+
+
+
+
+Click **Dimensions** to see a list of the attributes associated with these subscription events:
+
+![Subscription event dimensions](../../../images/ab-test/data-explorer-subscription-dimensions.png)
+
+You can filter and group subscription events using these dimensions. Notice the **NRQL** query above the chart. This shows the chart's underlying query, which is based on these dimensions.
+
+Click the **page-version** dimension to see the query change to group by `FACET page_version`:
+
+![Subscription event dimensions](../../../images/ab-test/data-explorer-subscription-page-version.png)
+
+The Data explorer presents two options for filtering and sorting your data:
+
+- User interface (UI) selections like the one you've just made
+- New Relic Query Language (NRQL)
+
+The UI is useful for quickly filtering data, and it doesn't require you to know NRQL. For your New Relic One application, however, you need to use NRQL queries.
+
+Click the **NRQL** query to navigate to the Query builder:
+
+![Subscription event dimensions](../../../images/ab-test/data-explorer-subscription-edit-nrql.png)
+
+Here, you can view and manually edit the query to fetch the data you need.
+
+
+
+
+
+## Update `NewsletterSignups` with a `NrqlQuery`
+
+Before you begin integrating New Relic data in your NR1 app, consult your design guide:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+Your New Relic One application has eight charts in total, including line charts, pie charts, and table charts. Each of these charts currently shows mock data, but they need to show real data to be useful. First, focus on querying data for the topmost chart: **Newsletter subscriptions per version**.
+
+With the query you've built in the Data explorer, you already have the data you need for this chart:
+
+![Query builder](../../../images/ab-test/query-builder.png)
+
+
+
+In your query, you fetch subscription totals (`SELECT count(*) FROM subscriptions`), group them by their page version (`FACET page_version`), and focus the timeseries to the past 30 minutes (`SINCE 30 MINUTES AGO TIMESERIES`).
+
+Explore our [documentation](https://docs.newrelic.com/docs/query-your-data/nrql-new-relic-query-language/get-started/introduction-nrql-new-relics-query-language) to learn more about NRQL queries.
+
+
+
+Next, you learn how to pass your NRQL query to your **Newsletter subscriptions per version** chart.
+
+
+
+
+
+Change to the _add-nrql-components_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-nrql-components
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet/newsletter-signups.js_, update the `LineChart` in `NewsletterSignups`. Remove the mock data, and use the NRQL query you built in the Data explorer:
+
+```js fileName=nerdlets/ab-test-nerdlet/newsletter-signups.js lineHighlight=2-6,8,16-25
+import React from 'react';
+import {
+ HeadingText,
+ LineChart,
+ NrqlQuery,
+} from 'nr1';
+
+const ACCOUNT_ID = 123456 //
+
+export default class NewsletterSignups extends React.Component {
+ render() {
+ return
+
+ Newsletter subscriptions per version
+
+
+ {
+ ({ data }) => {
+ return ;
+ }
+ }
+
+
+ }
+}
+```
+
+
+
+Make sure you replace `` with your actual New Relic [account id](https://docs.newrelic.com/docs/accounts/accounts-billing/account-setup/account-id).
+
+
+
+In `NrqlQuery`, you set two props:
+
+- `accountId`: The id for the account you query from
+- `query`: The query to perform
+
+With these, your NR1 app can query the data you want to show in your chart.
+
+
+
+There is a convenience prop for using NRQL to supply data to your charts, called `query`. If you'd rather not use the `NrqlQuery` component, try the `query` prop instead:
+
+```js
+}
+ query="SELECT count(*) FROM subscription FACET page_version SINCE 30 MINUTES AGO TIMESERIES"
+/>
+```
+
+Keep in mind you must still supply the `accountId`.
+
+
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/add-nrql-components/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+[View your changes](https://one.newrelic.com?nerdpacks=local):
+
+![Your New Relic One application showing real subscription data](../../../images/ab-test/nrql-query-chart.png)
+
+**Newsletter subscriptions by version** now shows real data from New Relic's database rather than the mock data you defined before. Notice that your chart pulls data when your application loads, but does not continue pulling data while the application is open. You can fix this by adding another prop.
+
+
+
+
+
+Add a `pollInterval`:
+
+```js fileName=nerdlets/ab-test-nerdlet/newsletter-signups.js lineHighlight=19
+import React from 'react';
+import {
+ HeadingText,
+ LineChart,
+ NrqlQuery,
+} from 'nr1';
+
+const ACCOUNT_ID = 123456 //
+
+export default class NewsletterSignups extends React.Component {
+ render() {
+ return
+
+ Newsletter subscriptions per version
+
+
+ {
+ ({ data }) => {
+ return ;
+ }
+ }
+
+
+ }
+}
+```
+
+
+
+Make sure you replace `` with your actual New Relic [account id](https://docs.newrelic.com/docs/accounts/accounts-billing/account-setup/account-id).
+
+
+
+The `pollInterval` is the number of milliseconds between chart refreshes. Each time the chart refreshes, it queries fresh data from New Relic. In this case, you refresh every minute.
+
+
+
+
+
+## Populate your `PieChart` with `subscription` event data
+
+Now that you've seen how to passed New Relic data to **Newsletter subscriptions per version**, it's time to move on to **Total subscriptions per version**. These two charts are similar in that they compare subscription event data grouped by version. The primary differences between **Newsletter subscriptions per version** and **Total subscriptions per version** are:
+
+- One is a line chart and one is a pie chart
+- One shows timeseries data and one shows all-time totals
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet/total-subscriptions.js_, update the `TestDistributions` component, removing the mock data and populating the `PieChart` with the same NRQL query you used for **Newsletter subscriptions per version** but with different `TIMESERIES` and `SINCE` clauses:
+
+```js fileName=nerdlets/ab-test-nerdlet/total-subscriptions.js lineHighlight=2-6,8,16-26
+import React from 'react';
+import {
+ HeadingText,
+ NrqlQuery,
+ PieChart,
+} from 'nr1';
+
+const ACCOUNT_ID = 123456 //
+
+export default class TotalSubscriptions extends React.Component {
+ render() {
+ return
+
+ Total subscriptions per version
+
+
+ {
+ ({ data }) => {
+ return
+ }
+ }
+
+
+ }
+}
+```
+
+
+
+Make sure you replace `` with your actual New Relic [account id](https://docs.newrelic.com/docs/accounts/accounts-billing/account-setup/account-id).
+
+
+
+You don't need the `TIMESERIES` clause because the pie chart only shows numerical data. You don't need the `SINCE` clause because **Total subscriptions per version** needs to show all-time subscription totals.
+
+
+
+
+
+With your nerdpack served locally, [view your application](https://one.newrelic.com?nerdpacks=local) to see your charts serving real data:
+
+![Your New Relic One application showing real subscription totals data](../../../images/ab-test/nr1-app-subscription-totals.png)
+
+**Total subscriptions per version** now shows all-time subscription totals from both versions of your demo application.
+
+
+
+
+
+Well done! You've configured some charts to query real subscription data from New Relic's database.
+
+## Populate charts with `pageView` event data
+
+Consider the remaining charts that still use mock data:
+
+- **Total unsubscriptions per version**
+- **Version A - Page views vs. subscriptions**
+- **Version B - Page views vs. subscriptions**
+- **Version A - Page views**
+- **Version B - Page views**
+- **Past tests**
+
+Some of these charts need to show page view data. Fortunately, your demo application creates a custom event for every page view like it does for subscriptions! Since **Version A - Page views vs. subscriptions** and **Version B - Page views vs. subscriptions** require data from two sources, ignore these for now and focus on **Version A - Page views** and **Version B - Page views**.
+
+
+
+
+
+In _page-views.js_, remove the mock data from `VersionPageViews` and use a `NrqlQuery` component to supply a query:
+
+```js fileName=nerdlets/ab-test-nerdlet/page-views.js lineHighlight=2-6,8,16-26
+import React from 'react';
+import {
+ HeadingText,
+ LineChart,
+ NrqlQuery,
+} from 'nr1';
+
+const ACCOUNT_ID = 123456 //
+
+export default class VersionPageViews extends React.Component {
+ render() {
+ return
+ }
+}
+```
+
+
+
+Make sure you replace `` with your actual New Relic [account id](https://docs.newrelic.com/docs/accounts/accounts-billing/account-setup/account-id).
+
+
+
+
+
+
+
+With your nerdpack served locally, [view your application](https://one.newrelic.com?nerdpacks=local) to see your charts serving real data:
+
+![Your New Relic One application showing real page view data](../../../images/ab-test/page-views-charts.png)
+
+In these new queries, you fetch `pageView` custom events instead of `subscription` events. You also use a `WHERE` clause to filter to a particular `page_version` rather than a `FACET` to group by `page_version`.
+
+
+
+
+
+Phew, that's a lot of queries, but your application looks great! You're now showing real data in four charts. Remember the two charts you ignored because they require data from two sources?
+
+- **Version A - Page views vs. subscriptions**
+- **Version B - Page views vs. subscriptions**
+
+You have to handle these differently than you did for the charts you've been dealing with because NRQL has no method for querying data from multiple sources. In the next lesson, you'll learn how to supply data to these two charts.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Customize NRQL data_](../nrql-customizations).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/persist-selected-version.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/persist-selected-version.mdx
new file mode 100644
index 000000000..722a214d9
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/persist-selected-version.mdx
@@ -0,0 +1,494 @@
+---
+path: '/build-apps/ab-test/persist-version'
+title: 'Persist the selected version'
+template: 'GuideTemplate'
+description: 'Persist the selected version'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add a section to end your test_](../end-test), before starting this one.
+
+
+
+In the previous lesson, you created a section of your application that allows you to pick the most effective design from your A/B test. The goal of this section is to end the test once you’ve selected a winner:
+
+![First version of your end test section](../../../images/ab-test/end-test-section-v1.png)
+
+When you select a version from this form, the selection does not persist in the `Select` component. Now, it’s time to implement that behavior.
+
+
+
+
+
+Change to the _persist-selected-version_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/persist-selected-version
+```
+
+
+
+
+
+In _end-test.js_, initialize `EndTestSection.state` with a default `selectedVersion` field:
+
+```js fileName=nerdlets/ab-test-nerdlet/end-test.js lineHighlight=29-35
+import React from 'react';
+import {
+ Button,
+ Grid,
+ GridItem,
+ HeadingText,
+ Select,
+ SelectItem,
+} from 'nr1';
+
+class VersionSelector extends React.Component {
+ render() {
+ return
+ }
+}
+
+class EndTestButton extends React.Component {
+ render() {
+ return
+ }
+}
+
+export default class EndTestSection extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ selectedVersion: 'A',
+ };
+
+ this.selectVersion = this.selectVersion.bind(this);
+ }
+
+ selectVersion(event, value) {
+ this.setState({ selectedVersion: value });
+ }
+
+ render() {
+ return
+
+
+ Pick the winner of your A/B test:
+
+
+
+
+
+
+ End test
+
+
+ }
+}
+```
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/persist-selected-version/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+[View your changes](https://one.newrelic.com?nerdpacks=local):
+
+![Your choice persists in the version selector](../../../images/ab-test/persist-selected-version-final.png)
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+Voila! When you select a new version as the winner of the A/B test, that version is reflected in the menu. However, when you press **End test**, nothing happens. In the next lesson, you'll create a confirmation dialog to protect yourself from prematurely ending your A/B test.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Present an end test confirmation modal_](../confirmation-modal).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/pie-charts.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/pie-charts.mdx
new file mode 100644
index 000000000..3af6b391b
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/pie-charts.mdx
@@ -0,0 +1,201 @@
+---
+path: '/build-apps/ab-test/pie-charts'
+title: 'Add pie charts'
+template: 'GuideTemplate'
+description: 'Add pie charts'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add your first chart_](../first-chart), before starting this one.
+
+
+
+You’ve begun building your A/B test application. So far, it consists of a single line chart, which represents the number of subscriptions your newsletter receives from each version of your website. To understand what you’ll build in this lesson, review the design guide to remember what charts come next:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+In the design, there are two pie charts beneath the line chart that you’ve already created. One represents the distribution of users who receive version A and version B. The other represents the distribution of successful requests from users who got version A and version B.
+
+
+
+
+
+Change to the _add-pie-charts_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-pie-charts
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet_, add two new Javascript files:
+
+- _total-subscriptions.js_
+- _total-cancellations.js_
+
+```sh
+touch total-subscriptions.js total-cancellations.js
+```
+
+
+
+
+
+In _total-subscriptions.js_, create a component, called `TotalSubscriptions`, which renders mocked subscription data:
+
+```js fileName=nerdlets/ab-test-nerdlet/total-subscriptions.js
+import React from 'react';
+import { PieChart } from 'nr1';
+
+export default class TotalSubscriptions extends React.Component {
+ render() {
+ const subscriptionsA = {
+ metadata: {
+ id: 'subscriptions-A',
+ name: 'Version A',
+ viz: 'main',
+ color: 'blue',
+ },
+ data: [
+ { y: 259 },
+ ],
+ }
+ const subscriptionsB = {
+ metadata: {
+ id: 'subscriptions-B',
+ name: 'Version B',
+ viz: 'main',
+ color: 'green',
+ },
+ data: [
+ { y: 318 },
+ ],
+ }
+ return
+ }
+}
+```
+
+Notice that the series `data` is formatted differently for `PieChart` than it was for `LineChart`. Because `PieChart` uses unidimensional data, its series only take y-values.
+
+
+
+
+
+In _total-cancellations.js_, create a component, called `TotalCancellations`, which renders mocked cancellation data:
+
+```js fileName=nerdlets/ab-test-nerdlet/total-subscriptions.js
+import React from 'react';
+import { PieChart } from 'nr1';
+
+export default class TotalCancellations extends React.Component {
+ render() {
+ const cancellationsA = {
+ metadata: {
+ id: 'cancellations-A',
+ name: 'Version A',
+ viz: 'main',
+ color: 'blue',
+ },
+ data: [
+ { y: 118 },
+ ],
+ }
+ const cancellationsB = {
+ metadata: {
+ id: 'cancellations-B',
+ name: 'Version B',
+ viz: 'main',
+ color: 'green',
+ },
+ data: [
+ { y: 400 },
+ ],
+ }
+ return
+ }
+}
+```
+
+
+
+
+
+In your Nerdlet's _index.js_ file, import your new components and update your Nerdlet's `render()` method:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=3-4,10-11
+import React from 'react';
+import NewsletterSignups from './newsletter-signups';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+
+
+
+
+ }
+}
+```
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/add-pie-charts/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+View your changes in [New Relic](https://one.newrelic.com?nerdpacks=local):
+
+![Your app with pie charts](../../../images/ab-test/pie-charts.png)
+
+Here, you see the `PieChart` components displayed in your application.
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+Your application is starting to take shape. You’ve created a line chart and two pie charts. For now, these charts use mocked data, but you’ll provide them real data in a later lesson. Before you focus on the data in your charts, however, you’ll learn how to add a table to your application so you can visualize data in a new way.
+
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. When you're ready, continue on to the next lesson: [_Add tables_](../table-charts).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/platform-state-context.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/platform-state-context.mdx
new file mode 100644
index 000000000..8deeebc80
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/platform-state-context.mdx
@@ -0,0 +1,304 @@
+---
+path: '/build-apps/ab-test/platform-state-context'
+title: 'Add PlatformStateContext to your nerdlet'
+template: 'GuideTemplate'
+description: 'Add PlatformStateContext to your nerdlet'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Fetch data from a third-party service_](../third-party-service), before starting this one.
+
+
+
+In this course, you're building a New Relic One application. This application shows telemetry data from a demo service that is running an A/B test so that it can reveal that data in charts, like a dashboard. Your New Relic application is different than a dashboard, however, because it does more than show New Relic data. It pulls external data, provides UI components and functionality, and even has its own small data stores. The purpose of this New Relic application is to present context so you can better understand the A/B test results and how those results tie in to your business objectives.
+
+So far, you've built all your charts, organized them for improved usability, provided them with real data, and more. There are some final improvements you can make, using Platform API components. In this lesson, you learn how to take use values in the New Relic platform state.
+
+
+
+
+
+Change to the _add-platform-state-context_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-platform-state-context
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet/newsletter-signups.js_, add a `PlatformStateContext.Consumer` to your `NewsletterSignups` component's `render()` method:
+
+```js fileName=nerdlets/ab-test-nerdlet/newsletter-signups.js lineHighlight=6,17-33
+import React from 'react';
+import {
+ HeadingText,
+ LineChart,
+ NrqlQuery,
+ PlatformStateContext,
+} from 'nr1';
+
+const ACCOUNT_ID = 123456 //
+
+export default class NewsletterSignups extends React.Component {
+ render() {
+ return
+ }
+}
+
+```
+
+`PlatformStateContext.Consumer` provides access to the platform's URL state. This state contains two important values for you to use in this context:
+
+- accountId
+- timeRange
+
+Notice that `NrqlQuery` uses a constant called `ACCOUNT_ID`. Instead of hardcoding an account identifier in your nerdlet, you can use `accountId` from the platform URL state.
+
+
+
+
+
+Use `platformState.accountId` in your `NrqlQuery`:
+
+```js fileName=nerdlets/ab-test-nerdlet/newsletter-signups.js lineHighlight=21
+import React from 'react';
+import {
+ HeadingText,
+ LineChart,
+ NrqlQuery,
+ PlatformStateContext,
+} from 'nr1';
+
+export default class NewsletterSignups extends React.Component {
+ render() {
+ return
+ }
+}
+```
+
+Now, `NewsletterSignups` uses `platformState.timeRange` instead of a hardcoded `SINCE` clause.
+
+
+
+While `NrqlQuery` components accept a convenient `timeRange` prop, not every component does. You can still use `timeRange` in other contexts by accessing `duration`, `begin_time`, or `end_time`:
+
+```jsx
+
+ {
+ (platformState) => { console.log(platformState.timeRange.duration); }
+ }
+
+```
+
+
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/persist-selected-version/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account. It also allows your app to make Nerdgraph requests on behalf of your account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+[View your application](https://one.newrelic.com?nerdpacks=local):
+
+![Your New Relic One application using platform state](../../../images/ab-test/platform-state.png)
+
+Your `NrqlQuery` is now using the account ID, which it got from platform state. It's also using the platform state's time range, but your chart probably still shows the last 30 minutes. Why? Where does the `timeRange` in platform state come from?
+
+The time picker sits on the right side of your application's navigation bar:
+
+![Your New Relic One application's time picker](../../../images/ab-test/time-picker.png)
+
+Change this value and see your chart update:
+
+![Your New Relic One application showing the last 6 hours of data](../../../images/ab-test/time-picked.png)
+
+
+
+
+
+Update `VersionPageViews`:
+
+```js fileName=nerdlets/ab-test-nerdlet/page-views.js lineHighlight=6,15-32
+import React from 'react';
+import {
+ HeadingText,
+ LineChart,
+ NrqlQuery,
+ PlatformStateContext,
+} from 'nr1';
+
+export default class VersionPageViews extends React.Component {
+ render() {
+ return
+ }
+}
+```
+
+Of all the charts in your New Relic One application, these three charts are the ones that should update with the time picker. The others, **Total subscriptions per version**, **Total cancellations per version**, **Version A - Page views vs. subscriptions**, **Version B - Page views vs. subscriptions**, show total values over time. So, hardcoding their `SINCE` clauses to `7 DAYS AGO` makes sense, as this is a reasonable time period for the purposes of this course.
+
+
+
+
+
+While still serving your nerdpack locally, view your NR1 app to see your charts updating with the time range you choose:
+
+![Your charts showing your selected time period](../../../images/ab-test/page-views-time-range.png)
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+Now that you're basing your queries off the platform state, some of your charts are dynamic in their time ranges. This is a great improvement because it allows you to adjust your charts to show data from any particular point in time, which is useful for tying data to business outcomes.
+
+The Platform API components offer a lot more functionality, too, including the ability to navigate the user to another place in New Relic. You'll learn how to do this in the next lesson.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Add navigation to your nerdlet_](../navigation).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/publish.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/publish.mdx
new file mode 100644
index 000000000..e737e4486
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/publish.mdx
@@ -0,0 +1,322 @@
+---
+path: '/build-apps/ab-test/publish'
+title: 'Publish your New Relic One application'
+template: 'GuideTemplate'
+description: 'Publish your New Relic One application'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Describe your app for the catalog_](../catalog), before starting this one.
+
+
+
+In the last lesson, you created catalog information for the A/B test app you've been building throughout this course. Now, it's time to publish your app to the New Relic One catalog and submit those catalog details.
+
+## Versions and tags
+
+Publishing an application requires two key pieces of information:
+
+- version
+- tag
+
+An application's version identifies the code it contains and is stored in the nerdpack's root-level _package.json_ file. Every time you modify code in your nerdpack and are ready to release it, you'll update the version in _package.json_. For example, if you fix some bugs in the first major version your application code, you might publish the changes under version `1.0.1`.
+
+An app version's tags describe its state. For example, version `0.0.1` of a work-in-progress application might be published with a `DEV` tag to indicate that it's in development.
+
+
+
+There are some rules governing how you can use tags and you can read about those in our tags documentation.
+
+
+
+## Publish your first application
+
+
+
+
+
+Change to the _publish_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/publish
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+Navigate to the root of your Nerdpack at `nru-programmability-course/publish/ab-test`.
+
+
+
+
+
+Generate your own app UUID:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+The UUID is used to identify your app in the New Relic One app registry. Because you're using code that we developed for this course, an application with the existing UUID already exists in the registry. By generating your own, you're now able to publish this application.
+
+
+
+The UUID also ties your app to your account and, therefore, allows the application to make Nerdgraph requests on behalf of the account that installed it.
+
+
+
+
+
+
+
+In _package.json_, set `version` to `1.0.0`:
+
+```json filename=package.json lineHighlight=4
+{
+ "private": true,
+ "name": "ab-test",
+ "version": "1.0.0",
+ "scripts": {
+ "start": "nr1 nerdpack:serve",
+ "test": "exit 0"
+ },
+ "nr1": {
+ "uuid": "2d923ba6-d231-4dd3-830f-b1923577a422"
+ },
+ "dependencies": {
+ "prop-types": "^15.6.2",
+ "react": "^16.6.3",
+ "react-dom": "^16.6.3"
+ },
+ "browserslist": [
+ "last 2 versions",
+ "not ie < 11",
+ "not dead"
+ ]
+}
+```
+
+New Relic One uses [semantic versioning](https://semver.org) and, under this convention, `1.0.0` signals the first major release. Now, you're ready to publish!
+
+
+
+
+
+Publish your New Relic One application:
+
+```sh
+nr1 nerdpack:publish -t DEV
+```
+
+That's it! You published your application to New Relic's registry. The `-t` parameter specifies a tag for your published version. Among other logs, you should see the following confirmation in your console:
+
+```sh
+[output] Publishing Nerdpack AbTest ({blue}9da77738-9cf6-43c7-9ba0-e3a8c6ac7380{plain})
+[output] {success}✔{plain} Nerdpack published successfully!
+[output] {success}✔{plain} Tagged {blue}9da77738-9cf6-43c7-9ba0-e3a8c6ac7380{plain} version {green}1.0.0{plain} as {green}DEV{plain}.
+```
+
+For now, you've tagged the 1.0.0 release as `DEV` because it's still a work in progress.
+
+
+
+
+
+View your app's registry information:
+
+```sh
+nr1 nerdpack:info
+[output] Id: {blue}9da77738-9cf6-43c7-9ba0-e3a8c6ac7380
+[output] Region: {muted}us
+[output] Account ID: 123456
+[output] Local version: 1.0.0
+[output] Subscription Model: OWNER_AND_ALLOWED
+[output] Version Count: 4
+[output]
+[output] Version Date Tags
+[output] ------- ------------- ------
+[output] 1.0.0 {muted}5 minutes ago {green}DEV
+```
+
+The results of this command detail the information stored in New Relic's registry for your application, including its UUID, version, and account ID.
+
+
+
+
+
+## View your application in the catalog
+
+Now that your application is published and tagged, you can view it in the New Relic One app catalog.
+
+
+
+
+
+Go to [New Relic](https://one.newrelic.com). Notice that you're not using the `?nerdpacks=local` querystring parameter. You don't need it anymore because you're not serving your app locally.
+
+
+
+
+
+From the homepage, navigate to **Apps**:
+
+![Navigate to Apps](../../../images/ab-test/nav-to-apps.png)
+
+From here, you can see your application under **New Relic One catalog**:
+
+![Your app in the catalog](../../../images/ab-test/app-in-catalog.png)
+
+Notice that this is different than when you locally served your app. Local apps and published apps you're subscribed to show under **Your apps** instead.
+
+
+
+
+
+Click on your app to see more details:
+
+![App details](../../../images/ab-test/dev-version.png)
+
+Notice the release date and app version. This page should show the information you created in the last lesson. It doesn't yet, because you haven't submitted that information to the catalog, and publishing your app doesn't do that for you.
+
+
+
+
+
+## Submit catalog information
+
+Even though you've published your application, there are some things that the catalog doesn't know about. These are the descriptions, screenshots, and metadata you created in the last lesson. `nr1 catalog` is used to submit and view this information.
+
+
+
+
+
+Submit your catalog information:
+
+```sh
+nr1 catalog:submit
+[output] Uploading screenshots...
+[output] {green}✔ {plain}Screenshots uploaded
+[output] {green}✔ {plain}Updated metadata for AbTest 1.0.0
+```
+
+If everything goes right, you should see a success message notifying you that the screenshots and metadata were updated.
+
+You may, however, get an error when submitting this information to the catalog:
+
+```sh
+[output] Uploading screenshots...
+[output] {error}› {plain}Error: {error}1 error while updating AbTest 1.0.0
+[output] {error}›
+[output] {error}› Invalid Version: Nerdpack version 1.0.0 not found. Have you run `nr1 nerdpack:publish` yet?
+[output] {error}› {plain}Code: UNKNOWN
+```
+
+If you do, try again in a minute or two. It can take a few seconds for the catalog to update with the new version of your application.
+
+If that doesn't work, make sure you published your app with `nr1 nerdpack:publish`.
+
+
+
+
+
+View your catalog information:
+
+```sh
+nr1 catalog:info
+[output] {purple}description: {plain}Nerdpack ab-test
+[output] {purple}details: {plain}Display test data for our newsletter subscription A/B test
+[output] {purple}displayName: {plain}AbTest
+[output] {purple}icon.url: {plain}https://nr3.nr-ext.net/artifact-index-production/a685fec2-29fb-40b0-9f65-4178...
+[output] {purple}previews.0.url: {plain}https://application-catalog-production.s3.us-east-2.amazonaws.com/nerdpacks/a...
+[output] {purple}releaseDate: {plain}2021-03-12T15:46:09.600138Z
+[output] {purple}repository: {plain}https://github.com/newrelic-experimental/nru-programmability-course
+[output] {purple}tagline: {plain}Win @ newsletter subscriptions
+[output] {purple}version: {plain}1.0.0
+[output] {purple}whatsNew.changes: {plain}Initial release! Includes:
+[output] - A variety of charts for understanding the test r...
+[output] {purple}whatsNew.version: {plain}1.0.0
+```
+
+All the information from _catalog_ shows here.
+
+
+
+
+
+View your app in the catalog:
+
+![Your app in the catalog with metadata](../../../images/ab-test/app-meta-in-catalog.png)
+
+Notice the tagline on the app's catalog entry.
+
+Click on the app to see more:
+
+![Your app details](../../../images/ab-test/app-about.png)
+
+Here, you see app details, a documentation tab, release notes, and screenshots.
+
+
+
+
+
+## Update your version tag
+
+Your app looks great in the catalog, complete with metadata, images, and documentation. Before, you tagged the app as `DEV` because all of the information wasn't ready for public consumption. Now, it is. It's time to update your version tag.
+
+
+
+
+
+Update your `1.0.0` app version from `DEV` to `STABLE`:
+
+```sh
+nr1 nerdpack:tag -t STABLE
+[output] {success}✔{plain} Tagged {blue}9da77738-9cf6-43c7-9ba0-e3a8c6ac7380{plain} version {green}1.0.0{plain} as {green}STABLE{plain}.
+```
+
+Without specifying a version, `nr1` uses the version specified in _package.json_. You can specify a version with the `-V` command. Learn more about `nerdpack:tag` with the `nr1 nerdpack:tag --help` command.
+
+
+
+
+
+View your app information:
+
+```sh
+nr1 nerdpack:info
+[output] Id: {blue}9da77738-9cf6-43c7-9ba0-e3a8c6ac7380
+[output] Region: {muted}us
+[output] Account ID: 123456
+[output] Local version: 1.0.0
+[output] Subscription Model: OWNER_AND_ALLOWED
+[output] Version Count: 1
+[output]
+[output] Version Date Tags
+[output] ------- ------------- ------
+[output] 1.0.0 {muted}30 minutes ago {green}STABLE
+```
+
+The app is now tagged with `STABLE`, indicating it's ready for public use.
+
+
+
+Even though the app is ready for public use, it's still only visible to users of your account. Other accounts can't see apps created by other private accounts unless those apps are added to the public New Relic One catalog.
+
+
+
+
+
+
+
+Now that your app is published and its metadata is submitted, you can subscribe your account to the app from the catalog. In the next lesson, you'll learn the ways to subscribe and unsubscribe to your new application.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Subscribe to your New Relic One application_](../subscribe).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/serve-app.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/serve-app.mdx
new file mode 100644
index 000000000..b3f05fb76
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/serve-app.mdx
@@ -0,0 +1,93 @@
+---
+path: '/build-apps/ab-test/serve-app'
+title: 'Serve your New Relic One application'
+template: 'GuideTemplate'
+description: 'Locally serve your New Relic One application'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Create a Nerdpack_](../create-nerdpack), before starting this one.
+
+
+
+When you build a New Relic One application, it's valuable to view it on the platform. This helps you design, implement, and debug your application in the environment it will eventually be published to. With `nr1`, you can launch a development server that hosts your application so that New Relic One can present it to you.
+
+
+
+
+
+From inside the Nerdpack's root directory, run `nerdpack:serve`:
+
+```bash
+nr1 nerdpack:serve
+```
+
+When the Nerdpack has succeeded building and your application is ready to view, you'll see a message with a link to New Relic One:
+
+```bash copyable=false
+[output] {green}✔{plain} Server ready! Test it at: {purple}https://one.newrelic.com/?nerdpacks=local
+[output] {blue}↩{plain} Server will reload automatically if you modify any file!
+```
+
+Notice the appended query parameter `nerdpacks=local`. This query parameter tells New Relic to show locally served Nerdpacks.
+
+
+
+If you don't see your application on the New Relic platform, make sure you've included `nerdpacks=local` in your querystring!
+
+
+
+
+
+
+
+Navigate to the provided url. From the home page, select **Apps** to view New Relic One applications:
+
+![Navigate to the applications view in New Relic](../../../images/ab-test/select-apps.png)
+
+
+
+
+
+Under the **Your apps** section, find your launcher, called "A/B Test Launcher":
+
+![Select your application's launcher](../../../images/ab-test/select-launcher.png)
+
+
+
+
+
+Select your launcher to see your root Nerdlet and its default welcome message:
+
+![Your application's default welcome message](../../../images/ab-test/default-message.png)
+
+
+
+
+
+Congratulations, you've served your first New Relic One application!
+
+Notice, in the `nerdpack:serve` output, that the server reloads when you change files in your Nerdpack. Give it a try by updating _nerdlets/ab-test-nerdlet/index.js_:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=5
+import React from 'react';
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
A/B test results
;
+ }
+}
+```
+
+Your app automatically refreshes to show the new heading:
+
+![Your application's new welcome message](../../../images/ab-test/new-message.png)
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Add chart components to your A/B test application_](../add-charts).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/subscribe.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/subscribe.mdx
new file mode 100644
index 000000000..9ded76437
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/subscribe.mdx
@@ -0,0 +1,140 @@
+---
+path: '/build-apps/ab-test/subscribe'
+title: 'Subscribe to your New Relic One application'
+template: 'GuideTemplate'
+description: 'Subscribe to your New Relic One application'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Publish your New Relic One application_](../publish), before starting this one.
+
+
+
+In the last lesson, you published your A/B test app to the New Relic One catalog. You then submitted your app's description, metadata, documentation, and screenshots. Now, it's time to subscribe your account to the published app.
+
+## Subscribe to your application
+
+Now that your application is published to the catalog, you can view it by visiting the **Apps** page of [New Relic](https://one.newrelic.com):
+
+![Navigate to Apps](../../../images/ab-test/nav-to-apps.png)
+
+From the catalog UI, you can subscribe to applications.
+
+
+
+
+
+From the catalog, click your app:
+
+![Your app in the catalog](../../../images/ab-test/app-meta-in-catalog.png)
+
+
+
+If you don't see your app in the catalog, you may have to refresh the page or clear your browser cache.
+
+
+
+This opens the app's **About** page and shows the details you submitted to the catalog in the last lesson:
+
+![App details](../../../images/ab-test/app-about.png)
+
+
+
+
+
+At the top right of this page, click **Add this app**:
+
+![Add this app](../../../images/ab-test/add-this-app.png)
+
+This opens the **Account access** view.
+
+
+
+
+
+From **Account access**, select the account you want to subscribe to the application and the channel to subscribe to. Finally, select **Update 1 account**:
+
+![Subscribe](../../../images/ab-test/subscribe.png)
+
+Here, you only had one version tag to subscribe to. In other projects, you may have other versions with other tags.
+
+
+
+Notice that the web UI uses the term "channel" instead of "tag". This is a relic of an older version of New Relic One apps. With later versions of `nr1`, we've moved toward "tags" terminology. Eventually, the UI will also use "tags".
+
+
+
+
+
+
+
+Navigate to the **Apps** page, and see the AbTest app under **Your apps**:
+
+![Your apps](../../../images/ab-test/your-apps.png)
+
+From there, you can click your app and use it like you did when you were serving it locally!
+
+
+
+If you don't see the app under **Your apps**, you may have to refresh the page or clear your browser cache.
+
+
+
+
+
+
+
+It's important to note that you can subscribe to applications in the catalog with the `nr1` CLI. Read [our documentation](publish-deploy#subscribe-or-unsubscribe-apps) to learn more.
+
+## Unsubscribe from your application
+
+The process for unsubscribing from an application is similar to that of subscribing.
+
+
+
+
+
+Click the "i" icon on your app launcher:
+
+![App details from the launcher](../../../images/ab-test/app-details-from-launcher.png)
+
+Here, you see the same app details you saw when your app was in the catalog.
+
+
+
+
+
+Click **Manage access**:
+
+![Manage access](../../../images/ab-test/manage-access.png)
+
+This opens the **Account access** view.
+
+
+
+
+
+From **Account access**, deselect the account you want to unsubscribe from the app, and select **Update 1 account**:
+
+![Unsubscribe](../../../images/ab-test/unsubscribe.png)
+
+If you navigate back to **Apps*, you will see your app in the catalog.
+
+
+
+
+
+Throughout this course, you've built a New Relic One application from the ground up. You've used the `nr1` CLI to create a nerdpack, launcher, and nerdlet. You've used many components from the SDK. You've learned how to publish, tag, subscribe, and unsubscribe to and from apps in the catalog. You've also learned how to submit metadata to the catalog.
+
+## Next steps
+
+Now that you know how to build a New Relic One application, you can read the SDK documentation to learn more about all the components you can use to create apps for your own purposes.
+
+
+
+This lesson is the final part of a course that teaches you how to build a New Relic One application from the ground up. Congratulations on making it to the end!
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/table-charts.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/table-charts.mdx
new file mode 100644
index 000000000..87a2258e8
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/table-charts.mdx
@@ -0,0 +1,177 @@
+---
+path: '/build-apps/ab-test/table-charts'
+title: 'Add tables'
+template: 'GuideTemplate'
+description: 'Add tables'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add pie charts_](../pie-charts), before starting this one.
+
+
+
+In each hands-on lesson in this course, you have been adding something new to your A/B test application. You’ve spun up a new launcher and added multiple chart components to your nerdlet. In this lesson, you’ll create two new `TableChart` components that display information about each version of your website, such as the ratio of customers who subscribe to those who unsubscribe. Review your design guide to understand where these tables will fit into your application:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+In this design guide, the `TableChart` components come after the pie charts you created in the last lesson.
+
+
+
+
+
+Change to the _add-tables_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/add-tables
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet_, add a new Javascript file named _totals.js_:
+
+```sh
+touch totals.js
+```
+
+
+
+
+
+In this new file, create a component called `VersionTotals` to hold your first `TableChart` and some mock data:
+
+```js fileName=nerdlets/ab-test-nerdlet/totals.js
+import React from 'react';
+import { TableChart } from 'nr1';
+
+export default class VersionTotals extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ const versionATotals = {
+ metadata: {
+ id: `totals-${this.props.version}`,
+ name: `Version ${this.props.version}`,
+ columns: ['name', 'count'],
+ },
+ data: [
+ {
+ name: 'Subscriptions',
+ count: 0
+ },
+ {
+ name: 'Page views',
+ count: 0
+ },
+ ],
+ }
+ return
+ }
+}
+```
+
+`TableChart` is different from `PieChart` and `LineChart` in three big ways. First of all, there are no `color` or `viz` metadata attributes in its series. `TableChart` has a standard visualization, so it ignores things like `color`, which don't apply. Second, the `TableChart` series have `columns` metadata attributes. `columns` determines the names of the columns on the table. Finally, `TableChart` series data use neither x nor y values. Instead, they use keys that match the table column and values that determine what goes into the row.
+
+Notice that `VersionTotals` takes a `version` prop and supplies it to the chart metadata's `id` and `name` fields. This allows you to use this same component to create a table for version A and B and eliminate duplicate code.
+
+
+
+
+
+In your Nerdlet's _index.js_ file, import your new component and update your Nerdlet's `render()` method:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=5,13-14
+import React from 'react';
+import NewsletterSignups from './newsletter-signups';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionTotals from './totals';
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+
+
+
+
+
+
+ }
+}
+```
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/add-tables/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+View your changes in [New Relic](https://one.newrelic.com?nerdpacks=local):
+
+![Your app with table charts](../../../images/ab-test/table-charts.png)
+
+Here, you see the `TableChart` components displayed in your application.
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+
+
+There is one more table in the design guide that records past tests. The columns for this table are:
+
+- **End Date:** The end date for the test
+- **Version A Description:** A description for version A
+- **Version B Description:** A description for version B
+- **Winner:** The version selected as the final design
+
+Try to build this table in your application with these column names and some fake data, without looking at a code sample. This practice will reinforce what you've learned in this lesson.
+
+If you need help, review the `add-a-chart-group` directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course), which corresponds to the next lesson in this course. This has the code for the `PastTests` component.
+
+
+
+Each chart you’ll add to your application throughout this course is unique in its type or in the data it presents. So far, you’ve created tables, pie charts, and a line chart, and they all function independently of one another. In the next lesson, you’ll create a couple more line charts, but this time they’ll function together.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. When you're ready, continue on to the next lesson: [_Add a chart group_](../chart-group).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/third-party-service.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/third-party-service.mdx
new file mode 100644
index 000000000..7e793b5fa
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/third-party-service.mdx
@@ -0,0 +1,198 @@
+---
+path: '/build-apps/ab-test/third-party-service'
+title: 'Fetch data from a third-party service'
+template: 'GuideTemplate'
+description: 'Fetch data from a third-party service'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Access NerdStorageVault from your nerdlet_](../nerdstoragevault), before starting this one.
+
+
+
+
+In previous lessons, you learned of a third-party service that you can use to fetch mock cancellation data for the **Total cancellations per version** chart in your New Relic One application. Even though the data in this service is fake, the real value of this lesson is learning how you can use third party services to supply data to your New Relic One application.
+
+If you make a request to the mock service with cancellation data (https://api.nerdsletter.net/cancellations) you'll see a response rejecting your request with a message that reads "Unauthorized":
+
+```sh
+curl https://api.nerdsletter.net/cancellations
+[output] Unauthorized
+```
+
+This is because the Nerdsletter API requires an `Authorization` header. More specifically, you must pass a bearer token of `ABC123` to gain authorized access to its data. If you make a request to the service with the header `Authorization: Bearer ABC123`, you'll get a successful response with the mocked cancellation data:
+
+```sh
+curl https://api.nerdsletter.net/cancellations -h 'Authorization: Bearer ABC123'
+[output] {"a": 15, "b": 78}
+```
+
+In the last lesson, you used `NerdGraph` to store this API token in your New Relic One application's `NerdStorageVault` data store. You also passed the token to your `TotalCancellations` component and logged its use to your browser's console. In this lesson, you follow up that log statement with a real request to the Nerdsletter API using your authorization token. Then, you supply the data from that external resource to your **Total cancellations per version** chart.
+
+
+
+
+
+Change to the _third-party-services_ directory of the [coursework repository](https://github.com/newrelic-experimental/nru-programmability-course):
+
+```sh
+cd nru-programmability-course/third-party-services
+```
+
+This directory contains the code that we expect your application to have at this point in the course. By navigating to the correct directory at the start of each lesson, you leave your custom code behind, thereby protecting yourself from carrying incorrect code from one lesson to the next.
+
+
+
+
+
+In nerdlets/ab-test-nerdlet/total-cancellations.js, make a request to `api.nerdsletter.net` with your API token. Save the results to state, and use that state in `render()`:
+
+```js fileName=nerdlets/ab-test-nerdlet/total-cancellations.js lineHighlight=9,14-42,46-70,79
+import React from 'react';
+import { HeadingText, PieChart } from 'nr1';
+
+export default class TotalCancellations extends React.Component {
+ constructor() {
+ super(...arguments);
+
+ this.state = {
+ cancellations: [],
+ lastToken: null
+ }
+ }
+
+ generateChartData(data) {
+ const cancellationsA = data ? data.a : 0;
+ const cancellationsB = data ? data.b : 0;
+
+ return [
+ {
+ metadata: {
+ id: 'cancellations-A',
+ name: 'Version A',
+ viz: 'main',
+ color: 'blue',
+ },
+ data: [
+ { y: cancellationsA },
+ ],
+ },
+ {
+ metadata: {
+ id: 'cancellations-B',
+ name: 'Version B',
+ viz: 'main',
+ color: 'green',
+ },
+ data: [
+ { y: cancellationsB },
+ ],
+ },
+ ]
+ }
+
+ componentDidUpdate() {
+ if (this.props.token && this.props.token != this.state.lastToken) {
+ console.log(`Requesting data with api token ${this.props.token}`)
+
+ fetch(
+ "https://api.nerdsletter.net/cancellations",
+ {headers: {"Authorization": `Bearer ${this.props.token}`}}
+ ).then(
+ (response) => {
+ if (response.status == 200) {
+ return response.json()
+ } else if (response.status == 401) {
+ console.error("Incorrect auth header")
+ } else {
+ console.error(response.text())
+ }
+ }
+ ).then(
+ (data) => {
+ if (data) {
+ this.setState({
+ cancellations: this.generateChartData(data),
+ lastToken: this.props.token
+ })
+ }
+ }
+ )
+ }
+ }
+
+ render() {
+ return
+
+ Total cancellations per version
+
+
+
+ }
+}
+```
+
+In this code, you initialize `TotalCancellations.state.cancellations` with zero for the y-value in each series instead of the previously hardcoded values. This helps to more realistically represent what the chart should show if your New Relic One app hasn't successfully requested data from the Nerdsletter API. Next, you use Javascript's `fetch()` function to make an HTTP request to the Nerdsletter API. You then pass your token in the request's `Authorization` header. If the request is successful, you update the cancellation data in `TotalCancellations.state` so that that data is reflected in the component's render method.
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/third-party-service/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account. It also allows your app to make Nerdgraph requests on behalf of your account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+[View your changes](https://one.newrelic.com?nerdpacks=local). If your token in `NerdStorageVault` is not "ABC123", then your console will show an error that reads, "Incorrect auth header":
+
+![Incorrect auth header](../../../images/ab-test/incorrect-auth-header.png)
+
+If you set the token to "ABC123", then **Total cancellations per version** updates to show the values from the third-party service:
+
+![Real data from the Nerdsletter API](../../../images/ab-test/nerdsletter-success.png)
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+Great job! You've come a long way from running `nr1 nerdpack:create` for the first time. Take a look back at your design guide to see that your application now has everything from an interface and data perspective that you planned from the beginning:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+You've created eight charts with varying styles and supplied them with dynamic data from multiple sources. You've learned about the New Relic One SDK and used many of its components. You've even gathered data from a third-party service and mixed it seemlessly with your New Relic data to provide a complete look at how the competing versions in your A/B test perform against each other.
+
+From here, there is only one more set of APIs in the New Relic One SDK that you've yet to get your hands on: Platform APIs. These will come in handy in improving the usability of your New Relic One application.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Add PlatformStateContext to your nerdlet_](../platform-state-context).
+
+
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/user-interface-components-intro.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/user-interface-components-intro.mdx
new file mode 100644
index 000000000..a1d2219dd
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/user-interface-components-intro.mdx
@@ -0,0 +1,28 @@
+---
+path: '/build-apps/ab-test/add-ui'
+title: 'Add user interface components to your application'
+template: 'GuideTemplate'
+description: 'Add user interface components to your application'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add a chart group_](../chart-group), before starting this one.
+
+
+
+If an application is organized, it is more readable and more understandable. In your A/B test application, you have a lot of charts, but no organization. In the next few lessons, you’ll use user interface components to bring some organization to your application and provide new functionality. First, you’ll use a `Grid` component to arrange your charts to match the layout in your design guide. Second, you’ll add headings to clarify what each chart represents. Third, you'll add descriptions for your A and B design versions to the top of your app. Finally, you’ll create a section for ending your A/B test.
+
+Before you add user any UI components to your application, remind yourself of how your application will look when you finish:
+
+![Design guide for chart components](../../../images/ab-test/charts-design-guide.png)
+
+In the next lesson, you arrange your charts to look like they do in your design guide.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. When you're ready, continue on to the next lesson: [_Add a grid_](../grid).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/create-an-ab-test-application/version-descriptions.mdx b/src/markdown-pages/build-apps/create-an-ab-test-application/version-descriptions.mdx
new file mode 100644
index 000000000..435d319fc
--- /dev/null
+++ b/src/markdown-pages/build-apps/create-an-ab-test-application/version-descriptions.mdx
@@ -0,0 +1,169 @@
+---
+path: '/build-apps/ab-test/version-descriptions'
+title: 'Add version descriptions'
+template: 'GuideTemplate'
+description: 'Add version descriptions'
+---
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. If you haven't already, check out the [course introduction](../../../ab-test).
+
+Each lesson in the course builds upon the last, so make sure you've completed the last lesson, [_Add chart headings_](../chart-headings), before starting this one.
+
+
+
+With your charts organized and descriptive headings above each one, your New Relic One application is becoming more usable. In this lesson, you'll continue that trend by creating descriptions for each design version in your A/B test.
+
+
+
+
+
+In _nerdlets/ab-test-nerdlet_, add a new Javascript file named _description.js_:
+
+```sh
+touch description.js
+```
+
+
+
+
+
+In this new file, create a new React component, called `VersionDescription`, which uses a `HeadingText` and a `BlockText` to render a version description that you pass, using the `description` prop:
+
+```js fileName=nerdlets/ab-test-nerdlet/description.js
+import React from 'react';
+import { BlockText, HeadingText } from 'nr1';
+
+export default class VersionDescription extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
+ Version {this.props.version}
+
+
+ {this.props.description}
+
+
+ )
+ }
+}
+```
+
+You'll use this one class to create a version description for each design version in your A/B test.
+
+
+
+
+
+In your Nerdlet's _index.js_ file, import `VersionDescription`, create descriptions for each design version, and create a new `GridItem` component for each design version:
+
+```js fileName=nerdlets/ab-test-nerdlet/index.js lineHighlight=7,11-12,18-30
+import React from 'react';
+import { ChartGroup, Grid, GridItem } from 'nr1';
+import NewsletterSignups from './newsletter-signups';
+import PastTests from './past-tests';
+import TotalCancellations from './total-cancellations';
+import TotalSubscriptions from './total-subscriptions';
+import VersionDescription from './description';
+import VersionPageViews from './page-views';
+import VersionTotals from './totals';
+
+const VERSION_A_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter"'
+const VERSION_B_DESCRIPTION = 'The newsletter signup message says, "Sign up for our newsletter and get a free shirt!"'
+
+export default class AbTestNerdletNerdlet extends React.Component {
+ render() {
+ return
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+}
+```
+
+Here, you've created two `VersionDescription` components. You passed the `description` and `version` props, which correspond to a design version.
+
+You also added a horizontal rule to visually separate the descriptions from the charts in your app. For this, you added a `GridItem` with a `columnSpan` of 12, to stretch the rule the full width of the grid.
+
+
+
+
+
+Navigate to the root of your Nerdpack at _nru-programmability-course/add-version-descriptions/ab-test_.
+
+
+
+
+
+Generate a new UUID for your Nerdpack:
+
+```sh
+nr1 nerdpack:uuid -gf
+```
+
+Because you cloned the coursework repository that contained an existing Nerdpack, you need to generate your own unique identifier. This UUID maps your Nerdpack to your New Relic account.
+
+
+
+
+
+Serve your application locally:
+
+```sh
+nr1 nerdpack:serve
+```
+
+
+
+
+
+[View your changes](https://one.newrelic.com?nerdpacks=local):
+
+![Your choice persists in the version selector](../../../images/ab-test/version-descriptions.png)
+
+When you're finished, stop serving your New Relic One application by pressing `CTRL+C` in the terminal window where you're serving your application.
+
+
+
+
+
+Now, you've added descriptions for your competing designs and your charts. In the next lesson, you’ll create a new section of your application from user interface components. This section will be used to end the A/B test with the click of a button.
+
+
+
+This lesson is part of a course that teaches you how to build a New Relic One application from the ground up. Continue on to the next lesson: [_Add a section to end your test_](../end-test).
+
+
\ No newline at end of file
diff --git a/src/markdown-pages/build-apps/permission-manage-apps.mdx b/src/markdown-pages/build-apps/permission-manage-apps.mdx
index c47b2b6af..df3108202 100644
--- a/src/markdown-pages/build-apps/permission-manage-apps.mdx
+++ b/src/markdown-pages/build-apps/permission-manage-apps.mdx
@@ -33,7 +33,7 @@ For accounts with New Relic One pricing, there are permissions differences for b
**Full users** have the Nerdpack manager role and have full capabilities for creating and managing New Relic One applications, as well as accessing all types of applications in the New Relic One catalog.
-A **basic user** can develop and view their own local New Relic One apps, but they cannot:
+A **basic user** can develop and view their own local New Relic One apps, but they ***cannot***:
- Subscribe other users to apps they’ve created.
- Access or manage apps in the New Relic One catalog.
diff --git a/src/markdown-pages/explore-docs/nr1-nerdpack.mdx b/src/markdown-pages/explore-docs/nr1-nerdpack.mdx
index e62489f0a..acae07cb6 100644
--- a/src/markdown-pages/explore-docs/nr1-nerdpack.mdx
+++ b/src/markdown-pages/explore-docs/nr1-nerdpack.mdx
@@ -18,16 +18,16 @@ To set up your Nerdpacks, use the commands below. You can click any command to s
| Command | Description |
|---|---|
-| [`nr1 nerdpack:build`](#nr1-nerdpackbuild) | Assembles your Nerdpack into bundles|
-| [`nr1 nerdpack:clone`](#nr1-nerdpackclone) | Clones a Nerdpack from a git repository. |
-| [`nr1 nerdpack:serve`](#nr1-nerdpackserve) | Serves your Nerdpack for testing and development purposes. |
-| [`nr1 nerdpack:uuid`](#nr1-nerdpackuuid) | Shows or regenerates the UUID of a Nerdpack. |
-| [`nr1 nerdpack:publish`](#nr1-nerdpackpublish) | Publishes your Nerdpack to New Relic. |
-| [`nr1 nerdpack:deploy`](#nr1-nerdpackdeploy) | Deploys a Nerdpack version to a specific channel. |
-| [`nr1 nerdpack:undeploy`](#nr1-nerdpackundeploy) | Undeploys a Nerdpack version from a specific channel. |
-| [`nr1 nerdpack:clean`](#nr1-nerdpackclean) | Cleans your development folders. |
-| [`nr1 nerdpack:validate`](#nr1-nerdpackvalidate) | Validates the contents of your Nerdpack. |
-| [`nr1 nerdpack:info`](#nr1-nerdpackinfo) | Shows the state of your Nerdpack in the New Relic's registry. |
+| [`nr1 nerdpack:build`](#nr1-nerdpackbuild) | Builds a Nerdpack |
+| [`nr1 nerdpack:clean`](#nr1-nerdpackclean) | Removes all built artifacts |
+| [`nr1 nerdpack:clone`](#nr1-nerdpackclone) | Clones a Nerdpack from a git repository |
+| [`nr1 nerdpack:info`](#nr1-nerdpackinfo) | Shows the state of your Nerdpack in the New Relic's registry |
+| [`nr1 nerdpack:publish`](#nr1-nerdpackpublish) | Publish this Nerdpack |
+| [`nr1 nerdpack:serve`](#nr1-nerdpackserve) | Serves your Nerdpack for testing and development purposes |
+| [`nr1 nerdpack:tag`](#nr1-nerdpacktag) | Tags a specific Nerdpack version |
+| [`nr1 nerdpack:untag`](#nr1-nerdpackuntag) | Removes a tag from the registry |
+| [`nr1 nerdpack:uuid`](#nr1-nerdpackuuid) | Shows or regenerates the UUID of a Nerdpack |
+| [`nr1 nerdpack:validate`](#nr1-nerdpackvalidate) | Validates artifacts inside your Nerdpack |
# Command details
@@ -35,203 +35,261 @@ To set up your Nerdpacks, use the commands below. You can click any command to s
## `nr1 nerdpack:build`
-### Builds a Nerdpack
-Runs a webpack process to assemble your Nerdpack into javascript and CSS bundles. As many other CLI commands, it should be run at the package.json level of your Nerdpack.
+Builds a nerdpack
### Usage
-`$ nr1 nerdpack:build OPTION`
+
+```sh
+nr1 nerdpack:build
+```
### Options
-| | |
+
+|||
|---|---|
-| `--extra-metadata-path=extra-metadata-path` | Specify a json file path with extra metadata. [default: extra-metadata.json] |
-| `--prerelease=prerelease` | If specififed, the value will be appended to the current version of generated files. ie: --prerelease=abc. Then the version will be "1.2.3-abc". |
-| `--profile=profile` | The authencation profile you want to use. |
-| `--verbose` | Adds extra information to the output. |
+| `--extra-metadata-path=extra-metadata-path` | [default: extra-metadata.json] specify a json file path with extra metadata |
+| `--prerelease=prerelease` | if specified, the value will be appended to the current version of generated files. ie: --prerelease=abc. Then the version will be "1.2.3-abc". |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
-
+### Description
-***
+Runs a webpack process to assemble your Nerdpack into javascript and css bundles. As many other CLI commands, it should be run at the `package.json` level of your Nerdpack. It will search for `nr1.json` files in your subdirectories, validate them, and try to build them. This also runs `nr1 nerdpack:validate` before starting the build process.
-## `nr1 nerdpack:clone`
+## `nr1 nerdpack:clean`
-### Clone an existing Nerdpack
-Duplicates an existing Nerdpack onto your local computer. You can clone an open source Nerdpack from [our Open Source GitHub repositories](https://opensource.newrelic.com/explore-projects/). After choosing a git repository, this command performs the following actions so that you can start using the Nerdpack:
-1. Clones the repository.
-2. Sets the repository as remote upstream.
-3. Installs all of its dependencies (using npm).
-4. Generates a new UUID using your profile, and commits it.
+Removes all built artifacts
### Usage
-`$ nr1 nerdpack:clone OPTION`
+
+```sh
+nr1 nerdpack:clean
+```
### Options
-| | |
+
+|||
|---|---|
-| `-r, --repo=REPO` | Repository location (either an HTTPS or SSH path). **(Required)** |
-| `-p, --path=PATH` | Determines the directory to clone to (defaults to the repository name). |
-| `-f, --force` | Replaces destination folder if it exists. |
-| `--profile=PROFILE`| The authentication profile you want to use. |
-| `--verbose` | Adds extra information to the output. |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
-
+### Description
-***
+Cleans and removes the content and the development folders (`dist/`, `tmp/`)
-## `nr1 nerdpack:serve`
-### Serve your Nerdpack locally
-Launches a server with your Nerdpack locally on the [New Relic One platform](https://one.newrelic.com/?nerdpacks=local), where it can be tested live.
+## `nr1 nerdpack:clone`
-To learn more about working with apps locally, see our guide on how to [serve, publish, and deploy documentation](/build-apps/publish-deploy/).
+Clones a nerdpack from a git repository
### Usage
-`$ nr1 nerdpack:serve`
+
+```sh
+nr1 nerdpack:clone
+```
### Options
-| | |
+
+|||
|---|---|
-| `--profile=PROFILE`| The authentication profile you want to use. |
-| `--verbose` | Adds extra information to the output. |
+| `-f`, `--force` | Replace destination folder if it exists |
+| `-p`, `--path=path` | Directory where to clone (defaults to the repository name) |
+| `-r`, `--repo=repo` | (required) Repository location (either an HTTPS or SSH path) |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
-
+### Description
-***
+Given a git repository, it performs all the actions so that you can start using it. This includes, in order:
-## `nr1 nerdpack:uuid`
-### Get your Nerdpack's UUID
-Prints the UUID (Universal Unique ID) of your Nerdpack, by default. The UUID determines what data the Nerdpack can access and who can subscribe to the Nerdpack. To deploy a Nerdpack you didn't make, you'll have to assign it a new UUID by using the `-g` or `--generate` option.
+1. Clone the repository
+2. Set the repository as remote upstream
+3. Install all of its dependencies (using npm)
+4. Generate a new UUID using your profile, and commit it
+
+## `nr1 nerdpack:info`
-For more details, see [our GitHub workshop](https://github.com/newrelic/nr1-workshop/blob/master/lab-cli/INSTRUCTIONS.md) on GitHub.
+Shows the state of your nerdpack in the new relic's registry
### Usage
-`$ nr1 nerdpack:uuid`
+
+```sh
+nr1 nerdpack:info
+```
### Options
-| | |
+
+|||
|---|---|
-| `--profile=PROFILE`| The authentication profile you want to use. |
-| `-f, --force` | If present, it will override the existing UUID without asking. |
-| `-g, --generate` | Generates a new UUID if not available. |
-| `--verbose` | Adds extra information to the output. |
+| `-a`, `--all` | show all versions |
+| `-i`, `--nerdpack-id=nerdpack-id` | get info from the specified Nerdpack instead of local one |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
-
+### Description
-***
+The default amount of versions shown is 10 but all versions can be shown if the `--all` (or `-a`) flag is used
## `nr1 nerdpack:publish`
-### Publish your Nerdpack
-Publishes your Nerdpack to New Relic.
-Please note:
-* If no additional parameters are passed in, this command will automatically deploy the Nerdpack onto the DEV channel.
-* If you want to specify your own list of deploy channels, add the `--channel` option. For example, `$ nr1 nerdpack:publish --channel BETA --channel STABLE`.
-* If you want to disable this behavior, add `-D` or `--skip-deploy` to the command. Then, you can use `nr1 nerdpack:deploy` to perform a deploy manually.
-
-For more on publishing and deploying, see [Deploy to New Relic One](/build-tools/new-relic-one-applications/publish-deploy).
+Publish this nerdpack
### Usage
-`$ nr1 nerdpack:publish`
+
+```sh
+nr1 nerdpack:publish
+```
### Options
-| | |
+
+|||
|---|---|
-| `-B, --skip-build` | Skips the previous build process. |
-| `-D, --skip-deploy` | Skips the following deploy process. |
-| `-c, --channel=DEV/BETA/STABLE` | Specifies the channel to deploys to. [default: STABLE] |
-| `-f, --force` | Forces the publish, overriding any existing version in the registry. |
-| `--dry-run` | Undergoes publishing process without actually publishing anything. |
-| `--extra-metadata-path=extra-metadata-path` | Specifies a json file .path with extra metadata. [default: extra-metadata.json] |
-| `--prerelease=STRING` | The value you enter will be appended to the current version of generated files. |
-| `--profile=PROFILE` | The authentication profile you want to use. |
-| `--verbose` | Adds extra information to the output. |
+| `-B`, `--skip-build` | skip the previous build process |
+| `-T`, `--skip-tag` | do not tag the version |
+| `-t`, `--tag=tag` | [default: STABLE] |
+| `--dry-run` | publish process without actually publishing anything. |
+| `--extra-metadata-path=extra-metadata-path` | [default: extra-metadata.json] specify a json file path with extra metadata |
+| `--prerelease=prerelease` | if specified, the value will be appended to the current version of generated files. ie: --prerelease=abc. Then the version will be "1.2.3-abc". |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
-
+### Description
-***
+This command will build and upload your Nerdpack to the registry.
+
+If no additional parameters are passed in, this command will automatically tag the nerdpack version as `STABLE`. If you want to disable this behavior, pass the `--skip-tag` flag to the command.
-## `nr1 nerdpack:deploy`
-### Deploy your Nerdpack to a channel
-Deploys a Nerdpack version to a specific channel (DEV, BETA, or STABLE). A channel can only have one Nerdpack version deployed to it at one time. If a channel has an existing Nerdpack associated with it, deploying a new Nerdpack version to that channel will undeploy the previous one.
+See `nr1 nerdpack:tag --help` for more info on tags.
+
+## `nr1 nerdpack:serve`
-For more on publishing and deploying, see [Deploy to New Relic One](/build-tools/new-relic-one-applications/publish-deploy).
+Serves your nerdpack for testing and development purposes
### Usage
-`$ nr1 nerdpack:deploy OPTION`
+
+```sh
+nr1 nerdpack:serve
+```
### Options
-| | |
+
+|||
|---|---|
-| `-c, --channel=DEV/BETA/STABLE` | Specifies the channel to deploy to. **(required)** |
-| `-i, --nerdpack-id=NERDPACK_ID` | Specifies the Nerdpack to deploy. By default, the command will use the one in package.json. |
-| `--from-version=VERSION` | Specifies which version to deploy. By default, the command will use the one in package.json. |
-| `--profile=PROFILE` | The authentication profile you want to use. |
-| `--verbose` | Adds extra information to the output. |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
-
+### Description
-***
+Launches a server with your local code, ready to be tested live on the platform.
+
+## `nr1 nerdpack:tag`
-## `nr1 nerdpack:undeploy`
-### Undeploy your Nerdpack
-Undeploys a Nerdpack version from a specific channel (for example, DEV, BETA, or STABLE).
+Tags a specific nerdpack version
### Usage
-`$ nr1 nerdpack:undeploy OPTION`
+
+```sh
+nr1 nerdpack:tag
+```
### Options
-| | |
+
+|||
|---|---|
-| `-c, --channel=DEV/BETA/STABLE` | Specifies the channel to undeploy from. **(required)** |
-| `-i, --nerdpack-id=NERDPACK_ID` | Specifies the Nerdpack to undeploy. By default, the command will use the one in package.json. |
-| `--profile=PROFILE` | The authentication profile you want to use. |
-| `--verbose` | Adds extra information to the output. |
+| `-V`, `--from-version=from-version` | version to tag. If it's not provided will use the one in package.json |
+| `-i`, `--nerdpack-id=nerdpack-id` | nerdpack uuid to deploy. If it's not provided will use the one in nr1.json |
+| `-t`, `--tag=tag` | [default: STABLE] tag name |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
-***
+### Description
-## `nr1 nerdpack:clean`
-### Removes all built artifacts
-Cleans and removes the content and the developtment folders (dist/, tmp/).
+A single version may have several tags, but each tag can only be applied to one version. Tagging a different version with an existing tag will untag the previous one.
+
+We recommend using `STABLE` to tag the versions you want to show to your subscribers. See the command `nr1 nerdpack:subscribe` for more info.
+
+### Aliases
+
+```sh
+$ nr1 nerdpack:deploy
+```
+
+## `nr1 nerdpack:untag`
+
+Removes a tag from the registry
### Usage
-`$ nr1 nerdpack:clean OPTION`
+
+```sh
+nr1 nerdpack:untag
+```
### Options
-| | |
+
+|||
|---|---|
-| `--profile=profile` | The authentication profile you want to use |
-| `--verbose` | Adds extra information to the output. |
+| `-i`, `--nerdpack-id=nerdpack-id` | nerdpack uuid to deploy. If it's not provided will use the one in nr1.json |
+| `-t`, `--tag=tag` | [default: STABLE] tag name |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
-***
+### Description
-## `nr1 nerdpack:validate`
-### Validates artifacts inside your Nerdpack
-Validates artifacts inside your Nerdpack.
+Specified tag will be removed. No files will be actually deleted.
+
+### Aliases
+
+```sh
+$ nr1 nerdpack:undeploy
+```
+
+## `nr1 nerdpack:uuid`
+
+Shows or regenerates the uuid of a nerdpack
### Usage
-`$ nr1 nerdpack:validate OPTION`
+
+```sh
+nr1 nerdpack:uuid
+```
### Options
-| | |
+
+|||
|---|---|
-| `-l, --force-local` | The authentication profile you want to use. |
-| `-r, --force-remote` | Force download of new schema files. |
-| `--profile=profile` | The authentication profile you want to uset. |
-| `--verbose` | Adds extra information to the output. |
+| `-f`, `--force` | if present, it will override the existing UUID without asking |
+| `-g`, `--generate` | generates a new UUID if not available |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
-***
+### Description
-## `nr1 nerdpack:Info`
-### Shows the state of your Nerdpack in the New Relic's registry
-Shows the state of your Nerdpack in the New Relic's registry. The default amount of versions shown is 10 but all versions can be shown if the --all (or -a) flag is used
+By default, prints the Universal Unique ID of the specified package. It also enables generation (or regeneration) of the UUID of your package, depending on the flags passed.
+## `nr1 nerdpack:validate`
+
+Validates artifacts inside your nerdpack
### Usage
-`$ nr1 nerdpack:info OPTION`
+
+```sh
+nr1 nerdpack:validate
+```
### Options
-| | |
+
+|||
|---|---|
-| `-a, --all ` | Show all versions. |
-| `-i, --nerdpack-id=nerdpack-id` | Get info from the specified Nerdpack instead of local one. |
-| `--profile=profile` | The authentication profile you want to use. |
-| `--verbose` | Adds extra information to the output. |
+| `-l`, `--force-local` | do not download new schema files |
+| `-r`, `--force-remote` | force download of new schema files |
+| `--profile=profile` | the authentication profile you want to use |
+| `--verbose` | adds extra information to the output |
+
+### Aliases
+
+```sh
+nr1 nerdpack:ls
+```
+
+```sh
+nr1 nerdpack:val
+```
\ No newline at end of file
diff --git a/src/pages/index.js b/src/pages/index.js
index ba1c04d2c..99e41d33e 100644
--- a/src/pages/index.js
+++ b/src/pages/index.js
@@ -56,7 +56,7 @@ const IndexPage = ({ data, pageContext, location }) => {
-
+
@@ -80,34 +80,29 @@ const IndexPage = ({ data, pageContext, location }) => {
>
- Terraform is a popular
- infrastructure-as-code software tool built by HashiCorp. You use
- it to provision all kinds of infrastructure and services,
- including New Relic alerts.
-
-
- In this guide, you learn how to set up New Relic alerts with
- Terraform. More specifically, you provision an alert policy,
- four alert conditions, and a notification channel.
+ Data Nerds, get ready to hack the future. Level Up your
+ observability game at{' '}
+ Futurestack 2021.
-
+ Rack up your experience points with new tools, new skills, and
+ whole new ways to play with your data through Observability.
+
+
+ Connect with Nerds from across the globe to learn, share, and
+ get inspired as we reinvent the future of software — and have
+ lots of fun doing it.
-
+
diff --git a/src/pages/nerdlog-thank-you.js b/src/pages/nerdlog-thank-you.js
index aaa334be1..94d3ef7e5 100644
--- a/src/pages/nerdlog-thank-you.js
+++ b/src/pages/nerdlog-thank-you.js
@@ -26,6 +26,18 @@ const NerdlogPage = ({ location }) => {
You will now be notified when we have episodes of the Nerdlog
and get information about previous episodes every week.
+