All files / packages/design-system/shared/components CodeBlock.jsx

88% Statements 22/25
62.5% Branches 10/16
66.66% Functions 2/3
87.5% Lines 21/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125                            3x   1x 3x                           3x 3x     3x               6x   6x       6x 6x         3x       3x     6x       3x 3x       3x 3x 3x   3x                                                                         1x           1x            
// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license
 
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom/server.browser';
import { beautify } from '../utils/beautify';
import classNames from 'classnames';
import Copy from './Copy';
import Prism from '../vendor/prism';
 
import '../vendor/prism/_prism.scss';
import '../vendor/prism/_prism-overrides.scss';
 
const highlight = (code, language) => Prism.highlight(code, Prism.languages[language]);
 
export const ToggleButton = (props) => (
  <div className="doc-toggle-code">
    <button
      {...props}
      className="slds-button doc-toggle-code__button"
      aria-pressed={props.open}
      aria-label="Show Code"
    >
      {props.open ? 'Hide ' : 'Show '} Code
    </button>
  </div>
);
 
class CodeBlock extends Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false,
    };
    this.toggleCodeBlock = this.toggleCodeBlock.bind(this);
  }
 
  toggleCodeBlock() {
    this.setState({ open: !this.state.open });
  }
 
  getCode(lines) {
    const { toggleCode, children } = this.props;
    try {
      React.Children.only(children);
    } catch (error) {
      throw new Error('We expected exactly one child in CodeView, you passed in 0 or many children');
    }
    const markup = children ? ReactDOM.renderToStaticMarkup(children) : '';
    const formatted = beautify(markup);
    let code;
 
    // Only reduce if # of lines is specified and the codeblock is meant to be toggled
    if (lines && toggleCode) {
      code = formatted
        .split(/\n/g)
        .reduce((codeLines, codeLine, i) => (i <= lines ? `${codeLines}\n` + codeLine : codeLines));
    } else {
      code = formatted;
    }
 
    return code;
  }
 
  getHighlightedCode(lines) {
    const { language } = this.props;
    return highlight(this.getCode(lines), language);
  }
 
  render() {
    const { language, toggleCode } = this.props;
    const { open } = this.state;
    const codeLines = open ? null : 3;
 
    return (
      <div className="docs-codeblock-source">
        <ul className="docs-codeblock__action-bar">
          <li>
            <Copy
              key="copy"
              className="slds-button_icon-container"
              containerClassName="site-code_copy"
              text={this.getCode()}
            />
          </li>
          {toggleCode && (
            <li>
              <ToggleButton open={this.state.open} onClick={this.toggleCodeBlock} />
            </li>
          )}
        </ul>
        <div
          className={classNames(
            'docs-codeblock-source__code',
            toggleCode && (this.state.open ? 'code-expanded' : 'code-collapsed'),
          )}
        >
          <pre className={`language-${language}`}>
            <code
              className={`language-${language}`}
              dangerouslySetInnerHTML={{
                __html: this.getHighlightedCode(codeLines),
              }}
            />
          </pre>
        </div>
      </div>
    );
  }
}
 
CodeBlock.propTypes = {
  language: PropTypes.string,
  toggleCode: PropTypes.bool,
  children: PropTypes.node,
};
 
CodeBlock.defaultProps = {
  language: 'html',
  toggleCode: true,
};
 
export default CodeBlock;