Skip to content

Commit

Permalink
syntax highlighting in action logger (storybookjs#118)
Browse files Browse the repository at this point in the history
* feat(highlight):added syntax highlighting in the action logger

* fix lint error

* fixed tests

* added tests for highlight
  • Loading branch information
ritz078 authored and wyattdanger committed Apr 26, 2016
1 parent 0a8d9bf commit 6b8a395
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 10 deletions.
12 changes: 7 additions & 5 deletions dist/client/ui/foldable.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ var _jsonStringifySafe = require('json-stringify-safe');

var _jsonStringifySafe2 = _interopRequireDefault(_jsonStringifySafe);

var _highlight = require('./highlight');

var _highlight2 = _interopRequireDefault(_highlight);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var folderStyle = {
Expand Down Expand Up @@ -124,11 +128,9 @@ var Foldable = function (_React$Component) {
this.state.collapsed ? '►' : '▼'
)
),
_react2.default.createElement(
'div',
{ ref: 'foldable-content', style: folderContentStyle },
content
)
_react2.default.createElement('div', { ref: 'foldable-content', style: folderContentStyle,
dangerouslySetInnerHTML: { __html: (0, _highlight2.default)(content) }
})
);
}
}]);
Expand Down
40 changes: 40 additions & 0 deletions dist/client/ui/highlight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = highlight;
/**
* Parses the JSON string and adds styling and class based on whether
* a part is string, number, undefined, null or key. Also removes quotes
* from keys.
*
* @param data A stringified JSON
* @returns {string} String with styling
*/
function highlight(data) {
var json = data.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
var regex = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g; // eslint-disable-line
return json.replace(regex, function (match) {
var className = 'number';
var style = void 0;
var result = match;
if (/^"/.test(result)) {
if (/:$/.test(result)) {
className = 'key';
style = 'color:#800080';
result = match.replace(/"/g, '');
} else {
className = 'string';
style = 'color:#a31515';
}
} else if (/true|false/.test(result)) {
className = 'boolean';
style = 'color:#066066';
} else if (/null|undefined/.test(result)) {
className = 'null';
style = 'color:#a31515';
}
return '<span class="' + className + '" style="' + style + '">' + result + '</span>';
});
}
4 changes: 2 additions & 2 deletions src/client/ui/__tests__/foldable.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('<Foldable />', function () {
args: 'things',
};

const compactString = '{"name":"test action","args":"things"}';
const compactString = '{name:"test action",args:"things"}';

const wrap = mount(<Foldable action={data} />);
const content = wrap.ref('foldable-content');
Expand All @@ -25,7 +25,7 @@ describe('<Foldable />', function () {
args: 'things',
};

const fullString = '{\n "name": "test action",\n "args": "things"\n}';
const fullString = '{\n name: "test action",\n args: "things"\n}';

const wrap = mount(<Foldable action={data} />);
const toggle = wrap.ref('foldable-toggle');
Expand Down
21 changes: 21 additions & 0 deletions src/client/ui/__tests__/highlight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { describe, it } = global;
import { expect } from 'chai';
import highlight from '../highlight';

describe('highlight', function () {
it('should remove quotes from keys and add correct colour', function () {
const data = '{ "name": "react-storybook" }';
const expected = '{ <span class="key" style="color:#800080">name:</span> <span class="string" style="color:#a31515">"react-storybook"</span> }'; // eslint-disable-line
expect(highlight(data)).to.equal(expected);
});

it('should preserve new lines also', function () {
const data = '{\n "name": "test action",\n "args": "things"\n}';
const expected = '{\n ' +
'<span class="key" style="color:#800080">name:</span> ' +
'<span class="string" style="color:#a31515">"test action"</span>,\n ' +
'<span class="key" style="color:#800080">args:</span> ' +
'<span class="string" style="color:#a31515">"things"</span>\n}';
expect(highlight(data)).to.equal(expected);
});
});
7 changes: 4 additions & 3 deletions src/client/ui/foldable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import stringify from 'json-stringify-safe';
import highlight from './highlight';

const folderStyle = {
display: 'block',
Expand Down Expand Up @@ -70,9 +71,9 @@ class Foldable extends React.Component {
{ this.state.collapsed ? '►' : '▼' }
</span>
</div>

<div ref="foldable-content" style={ folderContentStyle }>
{ content }
<div ref="foldable-content" style={ folderContentStyle }
dangerouslySetInnerHTML={ { __html: highlight(content) } }
>
</div>
</div>
);
Expand Down
34 changes: 34 additions & 0 deletions src/client/ui/highlight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Parses the JSON string and adds styling and class based on whether
* a part is string, number, undefined, null or key. Also removes quotes
* from keys.
*
* @param data A stringified JSON
* @returns {string} String with styling
*/
export default function highlight(data) {
const json = data.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
const regex = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g; // eslint-disable-line
return json.replace(regex, (match) => {
let className = 'number';
let style;
let result = match;
if (/^"/.test(result)) {
if (/:$/.test(result)) {
className = 'key';
style = 'color:#800080';
result = match.replace(/"/g, '');
} else {
className = 'string';
style = 'color:#a31515';
}
} else if (/true|false/.test(result)) {
className = 'boolean';
style = 'color:#066066';
} else if (/null|undefined/.test(result)) {
className = 'null';
style = 'color:#a31515';
}
return `<span class="${className}" style="${style}">${result}</span>`;
});
}

0 comments on commit 6b8a395

Please sign in to comment.