Skip to content

Commit

Permalink
feat: Add a Tutorial component that will flip between files and their…
Browse files Browse the repository at this point in the history
… code
  • Loading branch information
jerelmiller committed Oct 14, 2020
1 parent 211d8c9 commit 5d0fdc6
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 52 deletions.
4 changes: 4 additions & 0 deletions src/components/MDXContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import Important from './Important';
import Tip from './Tip';
import Intro from './Intro';
import Iframe from './Iframe';
import Tutorial from './Tutorial';
import TutorialStep from './TutorialStep';
import { MDXCodeBlock, Video } from '@newrelic/gatsby-theme-newrelic';

import styles from './MDXContainer.module.scss';
Expand All @@ -19,6 +21,8 @@ const components = {
Video,
Step,
Steps,
Tutorial,
TutorialStep,
Caution,
Important,
Tip,
Expand Down
53 changes: 53 additions & 0 deletions src/components/Tutorial.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { Children, cloneElement } from 'react';
import PropTypes from 'prop-types';
import parseCodeBlockProps from '../utils/parseCodeBlockProps';
import { isCodeBlock, isShellCommand } from '../utils/codeBlock';

const Tutorial = ({ children }) => {
const initialState = Children.toArray(children)
.flatMap((child) => Children.toArray(child.props.children))
.filter((child) => isCodeBlock(child) && !isShellCommand(child))
.reduce((map, child) => {
const { fileName, language } = parseCodeBlockProps(child);

return map.has(fileName)
? map
: map.set(fileName, { code: '', language });
}, new Map());

return Children.toArray(children).reduce((steps, stepElement, idx, arr) => {
const codeBlock = Children.toArray(stepElement.props.children).find(
(child) => isCodeBlock(child) && !isShellCommand(child)
);

if (!codeBlock) {
return [...steps, stepElement];
}

const previousStep =
idx === 0
? new Map(initialState)
: new Map(steps[idx - 1].props.step || initialState);

const { fileName, code, language } = parseCodeBlockProps(codeBlock);

return [
...steps,
cloneElement(stepElement, {
initialSelectedFile: fileName,
step: previousStep.set(fileName, { code, language }),
index: idx,
totalSteps: arr.length,
children: Children.toArray(stepElement.props.children).filter(
(child) => isShellCommand(child) || !isCodeBlock(child)
),
}),
];
}, []);
};

Tutorial.propTypes = {
children: PropTypes.node,
};

export default Tutorial;
71 changes: 71 additions & 0 deletions src/components/TutorialEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { CodeBlock } from '@newrelic/gatsby-theme-newrelic';
import path from 'path';
import { css } from '@emotion/core';
import { darken } from 'polished';

const TutorialEditor = ({ initialSelectedFile, files }) => {
const [selectedFile, setSelectedFile] = useState(initialSelectedFile);
const { language, code } = files.get(selectedFile);

return (
<div>
<div
css={css`
display: flex;
background: ${darken(0.05, '#2e3440')};
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
`}
>
{Array.from(files.keys()).map((fileName) => (
<button
key={fileName}
type="button"
onClick={() => setSelectedFile(fileName)}
css={css`
padding: 0.5rem 1rem;
cursor: pointer;
font-size: 0.75rem;
border: 0;
outline: 0;
color: currentColor;
background: ${fileName === selectedFile
? 'var(--color-nord-0)'
: 'inherit'};
`}
>
{path.basename(fileName)}
</button>
))}
</div>

{Array.from(files.entries()).map(([fileName, { code, language }]) => (
<CodeBlock
key={fileName}
lineNumbers
language={language}
fileName={fileName}
css={css`
display: ${fileName === selectedFile ? 'block' : 'none'};
> div:first-of-type {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
`}
>
{code}
</CodeBlock>
))}
</div>
);
};

TutorialEditor.propTypes = {
initialSelectedFile: PropTypes.string.isRequired,
files: PropTypes.instanceOf(Map).isRequired,
};

export default TutorialEditor;
71 changes: 71 additions & 0 deletions src/components/TutorialStep.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import PropTypes from 'prop-types';
import { css } from '@emotion/core';
import TutorialEditor from './TutorialEditor';
import Markdown from './Markdown';

const TutorialStep = ({
children,
initialSelectedFile,
step,
index,
title,
totalSteps,
}) => {
return (
<div
css={css`
&:not(:last-child) {
margin-bottom: 2rem;
}
`}
>
<p
css={css`
font-size: 0.75rem;
color: var(--accent-text-color);
margin-bottom: 0;
`}
>
Step {index + 1} of {totalSteps}
</p>
<h3
css={css`
font-size: 1rem;
font-weight: bold;
margin-top: 0 !important;
`}
>
<Markdown source={title} />
</h3>
{step ? (
<div
css={css`
display: grid;
grid-template-columns: repeat(2, calc(50% - 0.5rem));
grid-gap: 1rem;
`}
>
<div>{children}</div>
<TutorialEditor
files={step}
initialSelectedFile={initialSelectedFile}
/>
</div>
) : (
children
)}
</div>
);
};

TutorialStep.propTypes = {
children: PropTypes.node,
index: PropTypes.number.isRequired,
initialSelectedFile: PropTypes.string.isRequired,
step: PropTypes.instanceOf(Map).isRequired,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
totalSteps: PropTypes.number.isRequired,
};

export default TutorialStep;
81 changes: 29 additions & 52 deletions src/markdown-pages/build-apps/map-pageviews-by-region.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,14 @@ The following are some terms used in this guide:

## Build a custom app with a table chart

<Steps>

<Step>

## Query your browser data

Use Query builder to write a NRQL query to see your page view data, as follows.

1. On New Relic One, select **Query your data** (in the top right corner). That puts you in **NRQL** mode. You'll use NRQL to test your query before dropping the data into your table.

2. Copy and paste this query into a clear query field, and then select **Run**.
2. Copy and paste this query into a clear query field, and then select **Run**.

```sql
FROM PageView SELECT count(*), average(duration) WHERE appName = 'WebPortal' FACET countryCode, regionCode SINCE 1 week ago LIMIT 1000
```
Expand All @@ -76,78 +73,62 @@ If you have PageView data, this query shows a week of average page views broken

If you don't have any results at this point, ensure your query doesn't have any errors. If your query is correct, you might not have the [Browser agent](https://docs.newrelic.com/docs/browser/new-relic-browser/installation/install-new-relic-browser-agent) installed.

</Step>

<Step>
<Tutorial>

## Create and serve a new Nerdpack
<TutorialStep title="Create and serve a new Nerdpack">

To get started, create a new Nerdpack, and serve it up to New Relic from your local development environment:

1. Create a new Nerdpack for this app:

```
```bash
nr1 create --type nerdpack --name pageviews-app
```

2. Serve the project up to New Relic:

```
```bash
cd pageviews-app && nr1 nerdpack:serve
```

</Step>

<Step>
</TutorialStep>

## Review your app files and view your app locally
<TutorialStep title="Review your app files and view your app locally">

1. Navigate to your `pageviews-app` to see how it's structured.
It contains a launcher folder, where you can customize the description and icon that will be displayed on the app's launcher in New Relic One. It also contains `nerdlets`, which each contain three default files: `index.js`, `nr1.json`, and `styles.scss`. You'll edit some of these files as part of this guide. For more information, see [Nerdpack file structure](/explore-docs/nerdpack-file-structure).
1. Navigate to your `pageviews-app` to see how it's structured.
It contains a launcher folder, where you can customize the description and icon that will be displayed on the app's launcher in New Relic One. It also contains `nerdlets`, which each contain three default files: `index.js`, `nr1.json`, and `styles.scss`. You'll edit some of these files as part of this guide. For more information, see [Nerdpack file structure](/explore-docs/nerdpack-file-structure).

2. Now in your browser, open `https://one.newrelic.com/?nerdpacks=local`, and then click **Apps** to see the `pageview-apps` Nerdpack that you served up.
2. Now in your browser, open `https://one.newrelic.com/?nerdpacks=local`, and then click **Apps** to see the `pageview-apps` Nerdpack that you served up.

When you select the launcher, you see a `Hello` message.

</Step>

<Step>
</TutorialStep>

## Hard code your account ID
<TutorialStep title="Hard code your account ID">

For the purposes of this exercise and for your convenience, hard code your account ID. In the `pageview-app-nerdlet` directory, in the `index.js` file, add this code between the `import` and `export` lines. ([Read about finding your account ID here](https://docs.newrelic.com/docs/accounts/install-new-relic/account-setup/account-id)).

```jsx
```jsx fileName=pageview-app-nerdlet/index.js
const accountId = [Replace with your account ID];
```

</Step>

<Step>
</TutorialStep>

### Import the `TableChart` component
<TutorialStep title="Import the `TableChart` component">

To show your data in a table chart, import the [`TableChart` component](/client-side-sdk/index.html#charts/TableChart) from New Relic One. To do so, in `index.js`, add this code under `import React`.

```jsx
```jsx fileName=pageview-app-nerdlet/index.js
import { TableChart } from 'nr1';
```

</Step>

<Step>

### Add a table with a single row
</TutorialStep>

To add a table with a single row, in the `index.js` file, replace this line:
<TutorialStep title="Add a table with a single row">

```jsx
return <h1>Hello, pageview-app-nerdlet Nerdlet!</h1>;
```

with this `export` code:
To add a table with a single row, in the `index.js` file, replace this line with this `export` code:

```jsx
```jsx fileName=pageview-app-nerdlet/index.js
export default class PageViewApp extends React.Component {
render() {
return (
Expand All @@ -159,18 +140,16 @@ export default class PageViewApp extends React.Component {
}
```

</Step>
</TutorialStep>

<Step>

### Customize the look of your table (optional)
<TutorialStep title="Customize the look of your table (optional)">

You can use standard CSS to customize the look of your components.

In the `styles.scss` file, add this CSS.
Feel free to customize this CSS to your taste.

```css
```css fileName=styles.scss
.container {
width: 100%;
height: 99vh;
Expand All @@ -187,17 +166,15 @@ Feel free to customize this CSS to your taste.
}
```

</Step>
</TutorialStep>

<Step>

### Get your data into that table
<TutorialStep title="Get your data into that table">

Now that you've got a table, you can drop a `TableChart` populated with data from the NRQL query you wrote at the very beginning of this guide.

Put this code into the `row` div.

```jsx
```jsx fileName=pageview-app-nerdlet/index.js
<TableChart
accountId={accountId}
query={`FROM PageView SELECT count(*), average(duration) WHERE appName = 'WebPortal' SINCE 1 week ago LIMIT 1000`}
Expand All @@ -210,9 +187,9 @@ Go to New Relic One and click your app to see your data in the table. (You might

Congratulations! You made your app! Continue on to make it interactive and show your data on a map.

</Step>
</TutorialStep>

</Steps>
</Tutorial>

## Make your app interactive with a text field

Expand Down
Loading

0 comments on commit 5d0fdc6

Please sign in to comment.