Skip to content

Commit

Permalink
Merge pull request #129 from newrelic/jerel/multiple-code-blocks
Browse files Browse the repository at this point in the history
Better handling of multiple code blocks + code snippet styling
  • Loading branch information
timglaser authored Jun 12, 2020
2 parents 2020748 + 2088cc0 commit 7b82ffe
Show file tree
Hide file tree
Showing 17 changed files with 359 additions and 156 deletions.
136 changes: 85 additions & 51 deletions COMPONENT_GUIDE.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
# Globally available components


## Video

`<Video />` component provides formatting for videos in markdown.
`<Video />` component provides formatting for videos in markdown.

### Usage

### Usage
The Video component requires two props:

The Video component requires two props:
- `id`: the video ID
- `type`: the host of the video. Accepted values are:
- `wistia`
- `youtube`
- `wistia`
- `youtube`

```md
<Video id="zxunt1u1as" type="wistia"/>
```

## Intro

The `<Intro />` component provides formatting for the title and introduction of the markdown document.
The `<Intro />` component provides formatting for the title and introduction of the markdown document.

### Usage
It takes the title provided in the Frontmatter and accepts plain text for the description. An example of Frontmatter that will have a title of __Example Guide__:

It takes the title provided in the Frontmatter and accepts plain text for the description. An example of Frontmatter that will have a title of **Example Guide**:

```
---
Expand All @@ -34,9 +35,9 @@ description: 'Example guide page'
---
```

It also accepts a `<Video />` component as a child, which it will place on the left side of the description.
It also accepts a `<Video />` component as a child, which it will place on the left side of the description.

```md
```md
<Intro>
This is a description for the markdown guide.

Expand All @@ -46,35 +47,37 @@ This is a description for the markdown guide.

If there is a more than plain text and a `<Video />` (such as a code snippet or another component) the content will be posted on the left side below the description.


## Steps

The `<Steps />` is a required container for the individual `<Step />` components and will autonumber from top to bottom.
The `<Steps />` is a required container for the individual `<Step />` components and will autonumber from top to bottom.

### Usage
### Usage

The Steps component accepts `<Step/>` components as its children and will increment by the number of child components.

```md
```md
<Steps>

<Step>

## Step 1

Step 1 description.
</Step>

<Step>

## Step 2

Step 2 description
</Step>

</Steps>
```

If there is markdown content not wrapped by a `<Step />` component, it will still auto-increment for that content, so be sure to wrap all content with the Step component. For example:
```md
If there is markdown content not wrapped by a `<Step />` component, it will still auto-increment for that content, so be sure to wrap all content with the Step component. For example:

```md
<Steps>

<Step>
Expand All @@ -90,67 +93,98 @@ This will read as Step 3 of 3.
</Steps>
```

## Step
## Step

### Usage

The individual `<Step />` expects an optional h2 as the title of the step.
The `<Step />` component renders a single step in the series of steps. This is
meant to use in conjunction with `<Steps />`, so this component **MUST** be
wrapped by `<Steps />` to work properly. The following example will contain 2
steps:

```md
<Step>

## Step Title
</Step>
<Steps>
<Step>Some information about step 1</Step>
<Step>Some information about step 2</Step>
</Steps>
```

A second h2 tag will render as a normal H2 tag and not as the step title.
The previous example will interpret the text inside of the `<Step />` as plain
text. If you would like the `<Step />` component to interpret the text as
markdown content, include a line break after the opening tag:

```md
<Step>

## Step Title
## Normal h2 title
# This is a title for the step.

This is some information about this step.
</Step>
```
There must be a line break before the h2 tag or the component will not render it correctly. For example, this will __not__ render correctly:
```md

You can intersperse code blocks inside of the step. Code snippets will always
render in a right column next to the description.

````md
<Step>
## Step Title
</Step>

# A code example

Run the following command in your terminal:

```shell
npm start
```

Step will also take plain text as a description and a code snippet in standard three back tick form.
```md
</Step>
````

You can include multiple code blocks in a single step. Code blocks will always
be rendered to the right of the text that precedes it.

````md
<Step>

## Step Title
Step description
# Another code example

```shell
finishstep
```
</Step>
Run the following in your terminal:

```shell
npm start
```
The code snippet will always render to the right.

When that is running, edit index.js and replace the component with the following
code:

```js
return <div>Hello, {props.name}</div>;
```

</Step>
````

## Code Snippet

Code Snippets are automatically formatted by three backticks.
Code Snippets are automatically formatted by three backticks.

### Usage

There are three props that can be supplied to a code snippet.
There are three props that can be supplied to a code snippet.

- `language`: The first prop must be a code language. [Here](https://prismjs.com/#supported-languages) is a list of accepted languages for syntax highlighting.

- `language`: The first prop must be a code language. [Here](https://prismjs.com/#supported-languages) is a list of accepted languages for syntax highlighting.
````md
```jsx
```
````

```md
```jsx
```
- `lineNumbers`: `true` or `false`. Will show line numbers of the left side of the code, defaults to `true`.
```md
```jsx lineNumbers=false
```
- `lineNumbers`: `true` or `false`. Will show line numbers of the left side of the code, defaults to `true`.
````md
```jsx lineNumbers=false
```
````
- `copy`: `true` or `false`. Will display or not display the copy button, defaults to `true`
```md
```jsx copy=false
```
````md
```jsx copy=false
```
````
50 changes: 23 additions & 27 deletions src/components/CodeSnippet.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,48 @@
import React, { useState } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import Highlight, { defaultProps } from 'prism-react-renderer';
import github from 'prism-react-renderer/themes/github';
import FeatherIcon from './FeatherIcon';
import styles from './CodeSnippet.module.scss';
import cx from 'classnames';

const copyCode = (code, setCopied) => {
const textArea = document.createElement('textarea');
textArea.value = code;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
setCopied(true);
};
import useClipboard from '../hooks/useClipboard';
import useFormattedCode from '../hooks/useFormattedCode';

const CodeSnippet = ({ children, copy, className, lineNumbers }) => {
const language = className.replace('language-', '');
const [copied, setCopied] = useState(false);
const formattedCode = useFormattedCode(children ?? '');
const [copied, copyCode] = useClipboard();

return (
<div>
<div className={styles.container}>
<Highlight
{...defaultProps}
theme={github}
code={children}
code={formattedCode.trim()}
language={language}
>
{({ style, tokens, getLineProps, getTokenProps }) => (
<pre style={{ ...style, padding: '20px' }}>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line, key: i })}>
{lineNumbers !== 'false' && i < tokens.length - 1 && (
<span className={styles.lineNumber}>{i + 1}</span>
)}
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</div>
))}
<pre className={styles.codeContainer} style={style}>
<code>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line, key: i })}>
{lineNumbers !== 'false' && (
<span className={styles.lineNumber}>{i + 1}</span>
)}
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</div>
))}
</code>
</pre>
)}
</Highlight>
</div>
{copy !== 'false' && (
<div className={cx({ [styles.copied]: copied }, styles.copyBar)}>
<button type="button" onClick={() => copyCode(children, setCopied)}>
<div className={styles.copyBar}>
<button type="button" onClick={() => copyCode(formattedCode.trim())}>
<FeatherIcon name="copy" size="1rem" className={styles.copyIcon} />
{copied ? 'Copied!' : 'Copy output'}
</button>
</div>
Expand Down
33 changes: 13 additions & 20 deletions src/components/CodeSnippet.module.scss
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
.container {
height: 170px;
font-family: Menlo;
line-height: 1rem;
font-size: 0.75rem;
max-height: 26rem;
overflow: auto;
}

pre {
box-sizing: border-box;
margin: 0;
height: 100%;
overflow-y: auto;
padding: 20px 20px 20px 10px;
}
.codeContainer {
box-sizing: border-box;
margin: 0;
height: 100%;
overflow-y: auto;
padding: 1rem;
}

.lineNumber {
display: inline-block;
width: 20px;
width: 1.25rem;
text-align: right;
padding-right: 1em;
padding-right: 1rem;
user-select: none;
opacity: 0.5;
}
Expand All @@ -28,23 +28,16 @@
justify-content: flex-end;
align-items: center;
background: var(--color-neutrals-200);
height: 35px;
background-image: url('../images/copy.svg');
background-size: 1rem;
background-repeat: no-repeat;
background-position: 83% 50%;

button {
padding-right: 1rem;
font-size: 0.75rem;
font-family: var(--primary-font-family);
color: var(--color-brand-800);
background: none;
border: none;
outline: none;
}
}

.copied {
background-position: 88% 50%;
.copyIcon {
margin-right: 0.5rem;
}
Loading

0 comments on commit 7b82ffe

Please sign in to comment.